├── .github └── workflows │ └── tests.yml ├── .gitignore ├── LICENSE ├── MANIFEST.in ├── README.md ├── examples ├── 00-fibonacci.py ├── 01-coremark.py ├── 02-metered.py ├── 03-asyncified.py ├── pygame-audio.py ├── pygame-audio2.py ├── pygame-audio3.py ├── pygame-chip8.py ├── pygame-dino.py ├── pygame-doomfire.py ├── pygame-match3.py ├── pygame-maze.py ├── pygame-metaball.py ├── pygame-plop.py ├── pygame-raytrace.py ├── pygame-snake.py ├── pygame-wasmboy.py └── wasm │ ├── chip8.wasm │ ├── coremark-metered.wasm │ ├── coremark-minimal.wasm │ ├── dino.wasm │ ├── fire.wasm │ ├── hondarribia-22050.wasm │ ├── hondarribia-44100.wasm │ ├── match3.wasm │ ├── maze.wasm │ ├── metaball.wasm │ ├── music.wasm │ ├── plop-sim.wasm │ ├── plop-state.plop │ ├── ray.wasm │ ├── snake.wasm │ ├── wasmerboy.wasm │ └── wasmsummit2.wasm ├── m3module.c ├── setup.py ├── test ├── dyn_callback.c ├── helpers.py ├── test_dyn_callback.py ├── test_generic.py └── test_multivalue.py └── wasm3 ├── m3_bind.c ├── m3_bind.h ├── m3_code.c ├── m3_code.h ├── m3_compile.c ├── m3_compile.h ├── m3_config.h ├── m3_config_platforms.h ├── m3_core.c ├── m3_core.h ├── m3_env.c ├── m3_env.h ├── m3_exception.h ├── m3_exec.c ├── m3_exec.h ├── m3_exec_defs.h ├── m3_function.c ├── m3_function.h ├── m3_info.c ├── m3_info.h ├── m3_math_utils.h ├── m3_module.c ├── m3_parse.c ├── wasm3.h └── wasm3_defs.h /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: tests 2 | 3 | on: 4 | push: 5 | paths-ignore: ['**.md', '**.svg', '**.png'] 6 | pull_request: 7 | paths-ignore: ['**.md', '**.svg', '**.png'] 8 | 9 | jobs: 10 | python: 11 | runs-on: ubuntu-20.04 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | python-version: [ "3.6", "3.7", "3.8", "3.9" ] 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Set up Python ${{ matrix.python-version }} 20 | uses: actions/setup-python@v2 21 | with: 22 | python-version: ${{ matrix.python-version }} 23 | - name: Build Python module 24 | run: | 25 | pip install . 26 | - name: Install WABT 27 | run: | 28 | sudo apt install wabt 29 | - name: Lint 30 | run: | 31 | pip install flake8 32 | flake8 . --count --select=E7,E9,F63,F7,F82 --show-source --statistics 33 | - name: Test 34 | run: | 35 | pip install pytest 36 | pytest 37 | 38 | spellcheck: 39 | runs-on: ubuntu-latest 40 | steps: 41 | - name: Set up Python 42 | uses: actions/setup-python@v2 43 | - name: Install codespell 44 | run: | 45 | pip install codespell 46 | - name: Spellcheck 47 | run: | 48 | codespell 49 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | pywasm3.egg-info/ 3 | .pytest* 4 | __pycache__/ 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Volodymyr Shymanskyy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | graft wasm3 2 | include README.md 3 | include LICENSE 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner-direct.svg)](https://github.com/vshymanskyy/StandWithUkraine/blob/main/docs/README.md) 2 | 3 | # pywasm3 4 | 5 | Python bindings for Wasm3, a fast WebAssembly interpreter and the most universal WASM runtime. 6 | 7 | Main repository: [**Wasm3 project**](https://github.com/wasm3/wasm3) 8 | 9 | ## Install 10 | 11 | ```sh 12 | # Latest release: 13 | pip3 install pywasm3 14 | 15 | # Bleeding edge version: 16 | pip3 install https://github.com/wasm3/pywasm3/archive/main.tar.gz 17 | 18 | # Or, if you have a local copy: 19 | pip3 install . 20 | ``` 21 | 22 | ## Usage example 23 | 24 | ```py 25 | import wasm3, base64 26 | 27 | # WebAssembly binary 28 | WASM = base64.b64decode("AGFzbQEAAAABBgFgAX4" 29 | "BfgMCAQAHBwEDZmliAAAKHwEdACAAQgJUBEAgAA" 30 | "8LIABCAn0QACAAQgF9EAB8Dws=") 31 | 32 | env = wasm3.Environment() 33 | rt = env.new_runtime(2048) 34 | mod = env.parse_module(WASM) 35 | rt.load(mod) 36 | wasm_fib = rt.find_function("fib") 37 | result = wasm_fib(24) 38 | print(result) # 46368 39 | ``` 40 | 41 | ### License 42 | This project is released under The MIT License (MIT) 43 | -------------------------------------------------------------------------------- /examples/00-fibonacci.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import wasm3 4 | import base64, time, timeit 5 | 6 | """ 7 | Input module: 8 | (module 9 | (type (;0;) (func (param i64) (result i64))) 10 | (func (;0;) (type 0) (param i64) (result i64) 11 | local.get 0 12 | i64.const 2 13 | i64.lt_u 14 | if ;; label = @1 15 | local.get 0 16 | return 17 | end 18 | local.get 0 19 | i64.const 2 20 | i64.sub 21 | call 0 22 | local.get 0 23 | i64.const 1 24 | i64.sub 25 | call 0 26 | i64.add 27 | return) 28 | (export "fib" (func 0))) 29 | """ 30 | 31 | # WebAssembly binary 32 | WASM = base64.b64decode(""" 33 | AGFzbQEAAAABBgFgAX4BfgMCAQAHBwEDZmliAAAKHwEdACAAQgJUBEAgAA8LIABCAn0QACAAQgF9 34 | EAB8Dws= 35 | """) 36 | 37 | (N, RES, CYCLES) = (24, 46368, 1000) 38 | 39 | # Note: this is cold-start 40 | def run_wasm(): 41 | env = wasm3.Environment() 42 | rt = env.new_runtime(4096) 43 | mod = env.parse_module(WASM) 44 | rt.load(mod) 45 | wasm_fib = rt.find_function("fib") 46 | assert wasm_fib(N) == RES 47 | 48 | def fib(n: int) -> int: 49 | if n < 2: 50 | return n 51 | return fib(n-1) + fib(n-2) 52 | 53 | def run_py(): 54 | assert fib(N) == RES 55 | 56 | t1 = timeit.timeit(run_wasm, number=CYCLES) 57 | print(f"Wasm3: {t1:.4f} seconds") 58 | print("Cooling down... ", end="", flush=True) 59 | time.sleep(10) 60 | print("ok") 61 | t2 = timeit.timeit(run_py, number=CYCLES) 62 | if t2 > t1: 63 | ratio = f"{(t2/t1):.1f}x slower" 64 | else: 65 | retio = f"{(t1/t2):.1f}x faster" 66 | print(f"Python: {t2:.4f} seconds, {ratio}") 67 | 68 | -------------------------------------------------------------------------------- /examples/01-coremark.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import wasm3 4 | import os, time 5 | 6 | scriptpath = os.path.dirname(os.path.realpath(__file__)) 7 | wasm_fn = os.path.join(scriptpath, "./wasm/coremark-minimal.wasm") 8 | 9 | print("Initializing Wasm3 engine...") 10 | 11 | def clock_ms(): 12 | return int(round(time.time() * 1000)) 13 | 14 | env = wasm3.Environment() 15 | rt = env.new_runtime(4096) 16 | 17 | with open(wasm_fn, "rb") as f: 18 | mod = env.parse_module(f.read()) 19 | rt.load(mod) 20 | mod.link_function("env", "clock_ms", "I()", clock_ms) 21 | 22 | wasm_run = rt.find_function("run") 23 | 24 | print("Running CoreMark 1.0...") 25 | res = wasm_run() 26 | 27 | if res > 1: 28 | print(f"Result: {res:.3f}") 29 | else: 30 | print("Error") 31 | 32 | -------------------------------------------------------------------------------- /examples/02-metered.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import wasm3 4 | import os, time 5 | 6 | """ 7 | NOTE: Gas metering/limit only applies to pre-instrumented modules. 8 | You can generate a metered version from any wasm file automatically, using 9 | https://github.com/ewasm/wasm-metering 10 | """ 11 | 12 | scriptpath = os.path.dirname(os.path.realpath(__file__)) 13 | wasm_fn = os.path.join(scriptpath, "./wasm/coremark-metered.wasm") 14 | 15 | print("Initializing Wasm3 engine...") 16 | 17 | def clock_ms(): 18 | return int(round(time.time() * 1000)) 19 | 20 | env = wasm3.Environment() 21 | rt = env.new_runtime(4096) 22 | 23 | with open(wasm_fn, "rb") as f: 24 | mod = env.parse_module(f.read()) 25 | rt.load(mod) 26 | mod.link_function("env", "clock_ms", "I()", clock_ms) 27 | 28 | # Gas metering will only apply to metered (pre-instrumented) modules 29 | mod.gasLimit = 500_000_000 30 | 31 | wasm_run = rt.find_function("run") 32 | 33 | print("Running CoreMark 1.0...") 34 | try: 35 | res = wasm_run() 36 | 37 | if res > 1: 38 | print(f"Result: {res:.3f}") 39 | else: 40 | print("Error") 41 | finally: 42 | if mod.gasUsed: 43 | print(f"Gas used: {mod.gasUsed}") 44 | 45 | -------------------------------------------------------------------------------- /examples/03-asyncified.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import wasm3 4 | import base64, struct 5 | import asyncio 6 | 7 | """ 8 | This is a straightforward translation of JavaScript example from: 9 | https://kripken.github.io/blog/wasm/2019/07/16/asyncify.html 10 | 11 | Input module: 12 | (module 13 | (import "env" "before" (func $before)) 14 | (import "env" "sleep" (func $sleep (param i32))) 15 | (import "env" "after" (func $after)) 16 | (export "memory" (memory 0)) 17 | (export "main" (func $main)) 18 | (func $main 19 | (call $before) 20 | (call $sleep (i32.const 2000)) 21 | (call $after) 22 | ) 23 | (memory 1 1) 24 | ) 25 | 26 | Asyncify command: 27 | wasm-opt async.wasm --asyncify -O3 -o asyncified.wasm 28 | """ 29 | 30 | # WebAssembly binary 31 | WASM = base64.b64decode(""" 32 | AGFzbQEAAAABDANgAABgAX8AYAABfwImAwNlbnYGYmVmb3JlAAADZW52BXNsZWVwAAEDZW52BWFm 33 | dGVyAAADBgUAAQABAgUEAQEBAQYLAn8BQQALfwFBAAsHhAEHBm1lbW9yeQIABG1haW4AAxVhc3lu 34 | Y2lmeV9zdGFydF91bndpbmQABBRhc3luY2lmeV9zdG9wX3Vud2luZAAFFWFzeW5jaWZ5X3N0YXJ0 35 | X3Jld2luZAAGFGFzeW5jaWZ5X3N0b3BfcmV3aW5kAAUSYXN5bmNpZnlfZ2V0X3N0YXRlAAcK3gEF 36 | jAEBAX8CfyMAQQJGBEAjASMBKAIAQXxqNgIAIwEoAgAoAgAhAAsgAEVBASMAGwRAEABBACMAQQFG 37 | DQEaCyAAQQFGQQEjABsEQEHQDxABQQEjAEEBRg0BGgsgAEECRkEBIwAbBEAQAkECIwBBAUYNARoL 38 | DwshACMBKAIAIAA2AgAjASMBKAIAQQRqNgIACxkAQQEkACAAJAEjASgCACMBKAIESwRAAAsLFQBB 39 | ACQAIwEoAgAjASgCBEsEQAALCxkAQQIkACAAJAEjASgCACMBKAIESwRAAAsLBAAjAAs= 40 | """) 41 | 42 | # Init asyncio 43 | 44 | loop = asyncio.get_event_loop() 45 | 46 | def set_timeout(ms): 47 | def decorator(func): 48 | def wrapper(*args, **kwargs): 49 | return func(*args, **kwargs) 50 | loop.call_later(ms/1000, func) 51 | return wrapper 52 | return decorator 53 | 54 | 55 | # Prepare Wasm3 engine 56 | 57 | env = wasm3.Environment() 58 | rt = env.new_runtime(1024) 59 | mod = env.parse_module(WASM) 60 | rt.load(mod) 61 | mem = rt.get_memory(0) 62 | 63 | # ------------------------------------------------ 64 | def env_before(): 65 | print("before!") 66 | 67 | @set_timeout(1000) 68 | def callback(): 69 | print("(an event that happens during the sleep)") 70 | 71 | # ------------------------------------------------ 72 | def env_sleep(ms): 73 | global sleeping 74 | if not sleeping: 75 | print(f"sleep...") 76 | DATA_ADDR = 16 77 | mem[DATA_ADDR:DATA_ADDR+8] = struct.pack("= prebuffer*1024: 83 | buff_sz = 64 84 | draw("\n") 85 | 86 | if len(buff) >= buff_sz*1024: 87 | #draw("+") 88 | q.put(buff) 89 | buff = b'' 90 | time.sleep(0.01) 91 | 92 | return 0 93 | 94 | for modname in ["wasi_unstable", "wasi_snapshot_preview1"]: 95 | mod.link_function(modname, "fd_write", "i(i*i*)", fd_write) 96 | 97 | wasm_start = rt.find_function("_start") 98 | try: 99 | wasm_start() 100 | q.put(buff) # play the leftover 101 | except (KeyboardInterrupt, SystemExit): 102 | pass 103 | finally: 104 | q.put(None) 105 | q.close() 106 | p.join() 107 | 108 | print() 109 | print("Finished") 110 | -------------------------------------------------------------------------------- /examples/pygame-audio2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os, struct, time 4 | import multiprocessing as mp 5 | import wasm3 6 | import numpy 7 | 8 | os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "true" 9 | 10 | # Set to 44100 for better quality, or 11025 for faster computation 11 | sample_rate = 22050 12 | 13 | duration = 164000 14 | buffersize = 128*4 15 | 16 | prebuffer = 1024 17 | 18 | def draw(c): 19 | print(c, end='', flush=True) 20 | 21 | def player(q): 22 | import pygame 23 | pygame.mixer.pre_init(frequency=sample_rate, size=-16, channels=2) 24 | pygame.init() 25 | 26 | channel = pygame.mixer.Channel(0) 27 | try: 28 | while True: 29 | chunk = pygame.mixer.Sound(buffer=q.get()) 30 | 31 | draw("|" if channel.get_queue() else ".") 32 | 33 | while channel.get_queue() is not None: 34 | time.sleep(0.01) 35 | 36 | channel.queue(chunk) 37 | except (TypeError, BrokenPipeError, KeyboardInterrupt, SystemExit): 38 | pygame.quit() 39 | 40 | 41 | if __name__ == '__main__': 42 | 43 | print("WebAssembly Music by Peter Salomonsen - from the executable music competition at Revision demoparty 2021") 44 | print("Source: https://petersalomonsen.com/webassemblymusic/livecodev2/?gist=d71387112368a2692dc1d84c0ab5b1d2") 45 | print("Synthesized: https://soundcloud.com/psalomo/webassembly-music-entry-for-the-revision-2021-executable-music-competition") 46 | print() 47 | 48 | q = mp.Queue() 49 | p = mp.Process(target=player, args=(q,)) 50 | p.start() 51 | 52 | scriptpath = os.path.dirname(os.path.realpath(__file__)) 53 | wasm_fn = os.path.join(scriptpath, f"./wasm/music.wasm") 54 | 55 | # Prepare Wasm3 engine 56 | 57 | env = wasm3.Environment() 58 | rt = env.new_runtime(2*1024) 59 | with open(wasm_fn, "rb") as f: 60 | mod = env.parse_module(f.read()) 61 | rt.load(mod) 62 | 63 | mod.set_global("SAMPLERATE", sample_rate) 64 | 65 | wasm_play = rt.find_function("playEventsAndFillSampleBuffer") 66 | 67 | 68 | samplebufferL = mod.get_global("samplebuffer") 69 | samplebufferR = samplebufferL + buffersize 70 | 71 | def fetch_data(): 72 | global buff 73 | wasm_play() 74 | 75 | # get data 76 | mem = rt.get_memory(0) 77 | data_l = mem[samplebufferL : samplebufferL + buffersize] 78 | data_r = mem[samplebufferR : samplebufferR + buffersize] 79 | 80 | # decode 81 | data_l = numpy.frombuffer(data_l, dtype=numpy.float32) 82 | data_r = numpy.frombuffer(data_r, dtype=numpy.float32) 83 | data = numpy.dstack((data_l, data_r)) 84 | 85 | return (data.clip(-1,1) * 32767).astype(numpy.int16).tobytes() 86 | 87 | try: 88 | 89 | buff = b'' 90 | progress = 0 91 | while progress < 100: 92 | buff += fetch_data() 93 | progress = int(100*len(buff)/(prebuffer*1024)) 94 | if not progress % 5: 95 | draw(f"\rPre-buffering... {progress}%") 96 | 97 | q.put(buff) 98 | draw("\n") 99 | 100 | buff = b'' 101 | t = 0 102 | while t < duration: 103 | t = mod.get_global("currentTimeMillis") 104 | #draw(f"\rT: {t/1000:.3f}s") 105 | 106 | buff += fetch_data() 107 | 108 | if len(buff) >= 64*1024: 109 | #draw("+") 110 | q.put(buff) 111 | buff = b'' 112 | time.sleep(0.01) 113 | 114 | q.put(buff) # play the leftover 115 | except (KeyboardInterrupt, SystemExit): 116 | pass 117 | finally: 118 | q.put(None) 119 | q.close() 120 | p.join() 121 | 122 | print() 123 | print("Finished") 124 | -------------------------------------------------------------------------------- /examples/pygame-audio3.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os, struct, time 4 | import multiprocessing as mp 5 | import wasm3 6 | import numpy 7 | 8 | os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "true" 9 | 10 | # Set to 44100 for better quality, or 11025 for faster computation 11 | sample_rate = 22050 12 | 13 | buffersize = 128*4 14 | 15 | prebuffer = 256 16 | 17 | def draw(c): 18 | print(c, end='', flush=True) 19 | 20 | def player(q): 21 | import pygame 22 | pygame.mixer.pre_init(frequency=sample_rate, size=-16, channels=2) 23 | pygame.init() 24 | 25 | channel = pygame.mixer.Channel(0) 26 | try: 27 | while True: 28 | chunk = pygame.mixer.Sound(buffer=q.get()) 29 | 30 | draw("|" if channel.get_queue() else ".") 31 | 32 | while channel.get_queue() is not None: 33 | time.sleep(0.01) 34 | 35 | channel.queue(chunk) 36 | except (TypeError, BrokenPipeError, KeyboardInterrupt, SystemExit): 37 | pygame.quit() 38 | 39 | 40 | if __name__ == '__main__': 41 | 42 | print("WebAssembly Summit 2021 theme - by Peter Salomonsen") 43 | print("Source: https://petersalomonsen.com/webassemblymusic/livecodev2/?gitrepo=wasmsummit2") 44 | print() 45 | 46 | q = mp.Queue() 47 | p = mp.Process(target=player, args=(q,)) 48 | p.start() 49 | 50 | scriptpath = os.path.dirname(os.path.realpath(__file__)) 51 | wasm_fn = os.path.join(scriptpath, f"./wasm/wasmsummit2.wasm") 52 | 53 | # Prepare Wasm3 engine 54 | 55 | env = wasm3.Environment() 56 | rt = env.new_runtime(2*1024) 57 | with open(wasm_fn, "rb") as f: 58 | mod = env.parse_module(f.read()) 59 | rt.load(mod) 60 | 61 | mod.set_global("SAMPLERATE", sample_rate) 62 | 63 | wasm_play = rt.find_function("playEventsAndFillSampleBuffer") 64 | 65 | duration = rt.find_function("getDuration")() 66 | 67 | samplebufferL = mod.get_global("samplebuffer") 68 | samplebufferR = samplebufferL + buffersize 69 | 70 | def fetch_data(): 71 | global buff 72 | wasm_play() 73 | 74 | # get data 75 | mem = rt.get_memory(0) 76 | data_l = mem[samplebufferL : samplebufferL + buffersize] 77 | data_r = mem[samplebufferR : samplebufferR + buffersize] 78 | 79 | # decode 80 | data_l = numpy.frombuffer(data_l, dtype=numpy.float32) 81 | data_r = numpy.frombuffer(data_r, dtype=numpy.float32) 82 | data = numpy.dstack((data_l, data_r)) 83 | 84 | return (data.clip(-1,1) * 32767).astype(numpy.int16).tobytes() 85 | 86 | try: 87 | 88 | buff = b'' 89 | progress = 0 90 | while progress < 100: 91 | buff += fetch_data() 92 | progress = int(100*len(buff)/(prebuffer*1024)) 93 | if not progress % 5: 94 | draw(f"\rPre-buffering... {progress}%") 95 | 96 | q.put(buff) 97 | draw("\n") 98 | 99 | buff = b'' 100 | t = 0 101 | while t < duration: 102 | t = mod.get_global("currentTimeMillis") 103 | #draw(f"\rT: {t/1000:.3f}s") 104 | 105 | buff += fetch_data() 106 | 107 | if len(buff) >= 64*1024: 108 | #draw("+") 109 | q.put(buff) 110 | buff = b'' 111 | time.sleep(0.01) 112 | 113 | q.put(buff) # play the leftover 114 | except (KeyboardInterrupt, SystemExit): 115 | pass 116 | finally: 117 | q.put(None) 118 | q.close() 119 | p.join() 120 | 121 | print() 122 | print("Finished") 123 | -------------------------------------------------------------------------------- /examples/pygame-chip8.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import wasm3 4 | import os, time, random, math, base64 5 | import pygame 6 | 7 | print("WebAssembly demo file provided by Ben Smith (binji)") 8 | print("Sources: https://github.com/binji/raw-wasm") 9 | 10 | scriptpath = os.path.dirname(os.path.realpath(__file__)) 11 | wasm_fn = os.path.join(scriptpath, "./wasm/chip8.wasm") 12 | 13 | # Prepare Wasm3 engine 14 | 15 | env = wasm3.Environment() 16 | rt = env.new_runtime(1024) 17 | with open(wasm_fn, "rb") as f: 18 | mod = env.parse_module(f.read()) 19 | rt.load(mod) 20 | mod.link_function("Math", "random", random.random) 21 | 22 | wasm_run = rt.find_function("run") 23 | mem = rt.get_memory(0) 24 | 25 | # Load CHIP-8 ROM 26 | 27 | ROM = base64.b64decode(""" 28 | YwfB/6Js8R7wZUAAEgKEAMUfokz1HvBlyANIABImhkCGAkYAEgKjbIBg8FWEY6Js8R6AQPBVo2yC 29 | EIEygR6BHoEegiaCJoIm0SESAgMGDBgwYMCBBw4cOHDgwYMPHjx48OHDhx8+fPjx48eP//////// 30 | ////x/////////+D/////////wI/////////BB/4Z88TP/yED/mnjnN/+GgP+aeWcP/4GBP4ZzZw 31 | //AEIfmnAnN/4AJA+CE6cz/AAoH4YTsTP8BDAn//////gP4EP/////+A/wg5ydDD/8A/kHnJ05// 32 | wA/g+cnTn//gB+F5zLDH//ADEnnMs+P/+AMM+E5wh//8AwD4TnCH//wBAP///////gEA//////// 33 | AQH5zmEIR/8AA/nMYQnD/4AD+Eyzmcv/gAf4CbOYQ/8Hh/koE5nH/wOH+SnTmEP/AQf56dOYSf8B 34 | B////////wEH////////AQf//////wA= 35 | """) 36 | 37 | mem[0x200:0x200+len(ROM)] = ROM 38 | 39 | # Map memory region to an RGBA image 40 | 41 | img_base = 0x1000 42 | img_size = (64, 32) 43 | (img_w, img_h) = img_size 44 | region = mem[img_base : img_base + (img_w * img_h * 4)] 45 | img = pygame.image.frombuffer(region, img_size, "RGBA") 46 | 47 | # Prepare PyGame 48 | 49 | scr_size = (img_w*8, img_h*8) 50 | pygame.init() 51 | surface = pygame.display.set_mode(scr_size) 52 | pygame.display.set_caption("Wasm3 CHIP-8") 53 | white = (255, 255, 255) 54 | 55 | clock = pygame.time.Clock() 56 | 57 | while True: 58 | # Process input 59 | for event in pygame.event.get(): 60 | if (event.type == pygame.QUIT or 61 | (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE)): 62 | pygame.quit() 63 | quit() 64 | 65 | # TODO: input support 66 | #mem[10] = 0 67 | 68 | # Render next frame 69 | wasm_run(500) 70 | 71 | # Image output 72 | img_scaled = pygame.transform.scale(img, scr_size) 73 | surface.fill(white) 74 | surface.blit(img_scaled, (0, 0)) 75 | pygame.display.flip() 76 | 77 | # Stabilize FPS 78 | clock.tick(60) 79 | -------------------------------------------------------------------------------- /examples/pygame-dino.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import wasm3 4 | import os, time, random 5 | import pygame 6 | 7 | print("WebAssembly demo file provided by Ben Smith (binji)") 8 | print("Sources: https://github.com/binji/raw-wasm") 9 | 10 | scriptpath = os.path.dirname(os.path.realpath(__file__)) 11 | wasm_fn = os.path.join(scriptpath, "./wasm/dino.wasm") 12 | 13 | # Prepare Wasm3 engine 14 | 15 | env = wasm3.Environment() 16 | rt = env.new_runtime(1024) 17 | with open(wasm_fn, "rb") as f: 18 | mod = env.parse_module(f.read()) 19 | rt.load(mod) 20 | mod.link_function("Math", "random", lambda: random.random()) 21 | 22 | wasm_run = rt.find_function("run") 23 | mem = rt.get_memory(0) 24 | 25 | # Map memory region to an RGBA image 26 | 27 | img_base = 0x5000 28 | img_size = (300, 75) 29 | (img_w, img_h) = img_size 30 | region = mem[img_base : img_base + (img_w * img_h * 4)] 31 | img = pygame.image.frombuffer(region, img_size, "RGBA") 32 | 33 | # Prepare PyGame 34 | 35 | scr_size = (img_w*4, img_h*4) 36 | pygame.init() 37 | surface = pygame.display.set_mode(scr_size) 38 | pygame.display.set_caption("Wasm3 Dino") 39 | white = (255, 255, 255) 40 | 41 | k_jump = False 42 | k_duck = False 43 | 44 | clock = pygame.time.Clock() 45 | 46 | while True: 47 | # Process input 48 | for event in pygame.event.get(): 49 | if (event.type == pygame.QUIT or 50 | (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE)): 51 | pygame.quit() 52 | quit() 53 | elif event.type == pygame.KEYDOWN or event.type == pygame.KEYUP: 54 | if event.key == pygame.K_UP: 55 | k_jump = (event.type == pygame.KEYDOWN) 56 | elif event.key == pygame.K_DOWN: 57 | k_duck = (event.type == pygame.KEYDOWN) 58 | 59 | mem[0] = 0 60 | if k_jump: 61 | mem[0] |= 0x1 # Jump flag 62 | if k_duck: 63 | mem[0] |= 0x2 # Duck flag 64 | 65 | # Render next frame 66 | wasm_run() 67 | 68 | # Image output 69 | img_scaled = pygame.transform.scale(img, scr_size) 70 | surface.fill(white) 71 | surface.blit(img_scaled, (0, 0)) 72 | pygame.display.flip() 73 | 74 | # Stabilize FPS 75 | clock.tick(60) 76 | -------------------------------------------------------------------------------- /examples/pygame-doomfire.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import wasm3 4 | import os, time, random, math 5 | import pygame 6 | 7 | print("WebAssembly demo file provided by Ben Smith (binji)") 8 | print("Sources: https://github.com/binji/raw-wasm") 9 | 10 | scriptpath = os.path.dirname(os.path.realpath(__file__)) 11 | wasm_fn = os.path.join(scriptpath, "./wasm/fire.wasm") 12 | 13 | # Prepare Wasm3 engine 14 | 15 | env = wasm3.Environment() 16 | rt = env.new_runtime(1024) 17 | with open(wasm_fn, "rb") as f: 18 | mod = env.parse_module(f.read()) 19 | rt.load(mod) 20 | mod.link_function("", "rand", random.random) 21 | 22 | wasm_run = rt.find_function("run") 23 | mem = rt.get_memory(0) 24 | 25 | # Map memory region to an RGBA image 26 | 27 | img_base = 53760 28 | img_size = (320, 168) 29 | (img_w, img_h) = img_size 30 | region = mem[img_base : img_base + (img_w * img_h * 4)] 31 | img = pygame.image.frombuffer(region, img_size, "RGBA") 32 | 33 | # Prepare PyGame 34 | 35 | scr_size = (img_w*2, img_h*2) 36 | pygame.init() 37 | surface = pygame.display.set_mode(scr_size) 38 | pygame.display.set_caption("Wasm3 Doomfire") 39 | background = (255, 255, 255) 40 | 41 | clock = pygame.time.Clock() 42 | 43 | while True: 44 | # Process input 45 | for event in pygame.event.get(): 46 | if (event.type == pygame.QUIT or 47 | (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE)): 48 | pygame.quit() 49 | quit() 50 | 51 | # Render next frame 52 | wasm_run() 53 | 54 | # Image output 55 | img_scaled = pygame.transform.scale(img, scr_size) 56 | surface.fill(background) 57 | surface.blit(img_scaled, (0, 0)) 58 | pygame.display.flip() 59 | 60 | # Stabilize FPS 61 | clock.tick(60) 62 | -------------------------------------------------------------------------------- /examples/pygame-match3.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import wasm3 4 | import os, time, random 5 | import pygame 6 | 7 | print("WebAssembly demo file provided by Ben Smith (binji)") 8 | print("Sources: https://github.com/binji/raw-wasm") 9 | 10 | scriptpath = os.path.dirname(os.path.realpath(__file__)) 11 | wasm_fn = os.path.join(scriptpath, "./wasm/match3.wasm") 12 | 13 | # Prepare Wasm3 engine 14 | 15 | env = wasm3.Environment() 16 | rt = env.new_runtime(1024) 17 | with open(wasm_fn, "rb") as f: 18 | mod = env.parse_module(f.read()) 19 | rt.load(mod) 20 | mod.link_function("Math", "random", lambda: random.random()) 21 | 22 | wasm_run = rt.find_function("run") 23 | mem = rt.get_memory(0) 24 | 25 | # Map memory region to an RGBA image 26 | 27 | img_base = 0x1100 28 | img_size = (150, 150) 29 | (img_w, img_h) = img_size 30 | region = mem[img_base : img_base + (img_w * img_h * 4)] 31 | img = pygame.image.frombuffer(region, img_size, "RGBA") 32 | 33 | # Prepare PyGame 34 | 35 | scr_size = (img_w*4, img_h*4) 36 | pygame.init() 37 | surface = pygame.display.set_mode(scr_size) 38 | pygame.display.set_caption("Wasm3 Match3") 39 | white = (255, 255, 255) 40 | 41 | clock = pygame.time.Clock() 42 | 43 | prev_input = None 44 | prev_input_time = 0 45 | 46 | while True: 47 | # Process input 48 | for event in pygame.event.get(): 49 | if (event.type == pygame.QUIT or 50 | (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE)): 51 | pygame.quit() 52 | quit() 53 | 54 | (mouse_x, mouse_y) = pygame.mouse.get_pos() 55 | mem[0] = mouse_x // 4 56 | mem[1] = mouse_y // 4 57 | mem[2] = 1 if pygame.mouse.get_pressed()[0] else 0 58 | 59 | # Stop rendering if no interaction for 10 seconds 60 | inp = tuple(mem[0:3]) 61 | if inp != prev_input: 62 | prev_input_time = time.time() 63 | if time.time() - prev_input_time > 10: 64 | clock.tick(60) 65 | continue 66 | prev_input = inp 67 | 68 | # Render next frame 69 | wasm_run() 70 | 71 | # Image output 72 | img_scaled = pygame.transform.scale(img, scr_size) 73 | surface.fill(white) 74 | surface.blit(img_scaled, (0, 0)) 75 | pygame.display.flip() 76 | 77 | # Stabilize FPS 78 | clock.tick(60) 79 | -------------------------------------------------------------------------------- /examples/pygame-maze.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import wasm3 4 | import os, time, random, math 5 | import pygame 6 | 7 | print("WebAssembly demo file provided by Ben Smith (binji)") 8 | print("Sources: https://github.com/binji/raw-wasm") 9 | 10 | scriptpath = os.path.dirname(os.path.realpath(__file__)) 11 | wasm_fn = os.path.join(scriptpath, "./wasm/maze.wasm") 12 | 13 | def env_t(start): 14 | pass 15 | 16 | # Prepare Wasm3 engine 17 | 18 | env = wasm3.Environment() 19 | rt = env.new_runtime(1024) 20 | with open(wasm_fn, "rb") as f: 21 | mod = env.parse_module(f.read()) 22 | rt.load(mod) 23 | mod.link_function("Math", "sin", math.sin) 24 | mod.link_function("Math", "random", random.random) 25 | mod.link_function("env", "t", env_t) 26 | 27 | wasm_run = rt.find_function("run") 28 | mem = rt.get_memory(0) 29 | 30 | # Map memory region to an RGBA image 31 | 32 | img_base = 0x3000 33 | img_size = (320, 240) 34 | (img_w, img_h) = img_size 35 | region = mem[img_base : img_base + (img_w * img_h * 4)] 36 | img = pygame.image.frombuffer(region, img_size, "RGBA") 37 | 38 | # Prepare PyGame 39 | 40 | scr_size = (img_w*2, img_h*2) 41 | pygame.init() 42 | surface = pygame.display.set_mode(scr_size) 43 | pygame.display.set_caption("Wasm3 Maze") 44 | white = (255, 255, 255) 45 | 46 | k_up = False 47 | k_down = False 48 | k_left = False 49 | k_right = False 50 | 51 | clock = pygame.time.Clock() 52 | 53 | prev_input = None 54 | prev_input_time = 0 55 | 56 | while True: 57 | # Process input 58 | for event in pygame.event.get(): 59 | if (event.type == pygame.QUIT or 60 | (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE)): 61 | pygame.quit() 62 | quit() 63 | elif event.type == pygame.KEYDOWN or event.type == pygame.KEYUP: 64 | is_pressed = (event.type == pygame.KEYDOWN) 65 | if event.key == pygame.K_UP: 66 | k_up = is_pressed 67 | elif event.key == pygame.K_DOWN: 68 | k_down = is_pressed 69 | elif event.key == pygame.K_LEFT: 70 | k_left = is_pressed 71 | elif event.key == pygame.K_RIGHT: 72 | k_right = is_pressed 73 | 74 | mem[0] = k_left 75 | mem[1] = k_right 76 | mem[2] = k_up 77 | mem[3] = k_down 78 | 79 | # Stop rendering if no interaction for 10 seconds 80 | inp = tuple(mem[0:3]) 81 | if inp != prev_input: 82 | prev_input_time = time.time() 83 | if time.time() - prev_input_time > 10: 84 | clock.tick(60) 85 | continue 86 | prev_input = inp 87 | 88 | # Render next frame 89 | wasm_run() 90 | 91 | # Image output 92 | img_scaled = pygame.transform.scale(img, scr_size) 93 | surface.fill(white) 94 | surface.blit(img_scaled, (0, 0)) 95 | pygame.display.flip() 96 | 97 | # Stabilize FPS 98 | clock.tick(60) 99 | -------------------------------------------------------------------------------- /examples/pygame-metaball.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import wasm3 4 | import os, time, random, math 5 | import pygame 6 | 7 | print("WebAssembly demo file provided by Ben Smith (binji)") 8 | print("Sources: https://github.com/binji/raw-wasm") 9 | 10 | scriptpath = os.path.dirname(os.path.realpath(__file__)) 11 | wasm_fn = os.path.join(scriptpath, "./wasm/metaball.wasm") 12 | 13 | # Prepare Wasm3 engine 14 | 15 | env = wasm3.Environment() 16 | rt = env.new_runtime(1024) 17 | with open(wasm_fn, "rb") as f: 18 | mod = env.parse_module(f.read()) 19 | rt.load(mod) 20 | mod.link_function("", "rand", "f()", random.random) 21 | wasm_blobs = rt.find_function("blobs") 22 | wasm_blobs(5) 23 | 24 | wasm_run = rt.find_function("run") 25 | mem = rt.get_memory(0) 26 | 27 | # Map memory region to an RGBA image 28 | 29 | img_base = 1024 30 | img_size = (320, 200) 31 | (img_w, img_h) = img_size 32 | region = mem[img_base : img_base + (img_w * img_h * 4)] 33 | img = pygame.image.frombuffer(region, img_size, "RGBA") 34 | 35 | # Prepare PyGame 36 | 37 | scr_size = (img_w*2, img_h*2) 38 | pygame.init() 39 | surface = pygame.display.set_mode(scr_size) 40 | pygame.display.set_caption("Wasm3 Metaball") 41 | background = (0xd4, 0x19, 0x5d) 42 | 43 | clock = pygame.time.Clock() 44 | 45 | while True: 46 | # Process input 47 | for event in pygame.event.get(): 48 | if (event.type == pygame.QUIT or 49 | (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE)): 50 | pygame.quit() 51 | quit() 52 | 53 | # Render next frame 54 | wasm_run() 55 | 56 | # Image output 57 | img_scaled = pygame.transform.scale(img, scr_size) 58 | surface.fill(background) 59 | surface.blit(img_scaled, (0, 0)) 60 | pygame.display.flip() 61 | 62 | # Stabilize FPS 63 | clock.tick(30) 64 | -------------------------------------------------------------------------------- /examples/pygame-plop.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import wasm3 4 | import os, time, random, math 5 | import struct 6 | import pygame 7 | import zlib 8 | 9 | print("WebAssembly file by Caltrop") 10 | print("Sources: https://github.com/Caltrop256/plop") 11 | 12 | scriptpath = os.path.dirname(os.path.realpath(__file__)) 13 | wasm_fn = os.path.join(scriptpath, "./wasm/plop-sim.wasm") 14 | 15 | # Prepare Wasm3 engine 16 | 17 | env = wasm3.Environment() 18 | rt = env.new_runtime(32*1024) 19 | with open(wasm_fn, "rb") as f: 20 | mod = env.parse_module(f.read()) 21 | rt.load(mod) 22 | mod.link_function("env", "sin", "F(F)", math.sin) 23 | mod.link_function("env", "cos", "F(F)", math.cos) 24 | mod.link_function("env", "atan2", "F(FF)", math.atan2) 25 | 26 | wasm_seed = rt.find_function("seed") 27 | wasm_draw = rt.find_function("draw") 28 | wasm_tick = rt.find_function("tick") 29 | wasm_malloc = rt.find_function("malloc") 30 | wasm_free = rt.find_function("free") 31 | wasm_importData = rt.find_function("importData") 32 | 33 | 34 | wasm_seed(random.getrandbits(32), 35 | random.getrandbits(32), 36 | random.getrandbits(32), 37 | random.getrandbits(32), 38 | random.getrandbits(32), 39 | random.getrandbits(32)) 40 | 41 | # Load PLOP state 42 | 43 | state_fn = os.path.join(scriptpath, "./wasm/plop-state.plop") 44 | 45 | with open(state_fn, 'rb') as compressed: 46 | plop_data = zlib.decompress(compressed.read()) 47 | plop_len = len(plop_data) 48 | ptr = wasm_malloc(plop_len) 49 | 50 | mem = rt.get_memory(0) 51 | mem[ptr : ptr+plop_len] = plop_data 52 | 53 | res = wasm_importData(ptr) 54 | wasm_free(ptr) 55 | 56 | 57 | if res != 1: 58 | print("Invalid PLOP file") 59 | quit() 60 | 61 | # Map memory region to an RGBA image 62 | 63 | area_size = plop_data[8] 64 | img_size = (area_size * 75, area_size * 75) 65 | (img_w, img_h) = img_size 66 | 67 | # Prepare PyGame 68 | 69 | scr_size = (img_w*2, img_h*2) 70 | pygame.init() 71 | surface = pygame.display.set_mode(scr_size) 72 | pygame.display.set_caption("Wasm3 plop") 73 | white = (255, 255, 255) 74 | 75 | mem = rt.get_memory(0) 76 | img_ptr = mod.get_global("imageData") 77 | (img_base,) = struct.unpack("= 2: 13 | rom_fn = sys.argv[1] 14 | else: 15 | rom_fn = "" 16 | 17 | print("WasmBoy by Aaron Turner (torch2424)") 18 | print() 19 | 20 | if rom_fn: 21 | rom_f = open(rom_fn, "rb") 22 | rom_size = os.path.getsize(rom_fn) 23 | else: 24 | print('Downloading "Back to Color" demo by Antonio Niño Díaz...') 25 | try: 26 | rom_f = urllib.request.urlopen('https://github.com/AntonioND/back-to-color/raw/master/demo.gbc') 27 | rom_size = int(rom_f.headers['content-length']) 28 | except Exception: 29 | print('Download failed. Please specify GameBoy ROM file to run.') 30 | sys.exit(1) 31 | 32 | # Detect GameBoy Color ROM 33 | 34 | rom_data = rom_f.read() 35 | rom_is_color = (rom_data[0x0143] != 0) 36 | rom_f = io.BytesIO(rom_data) 37 | 38 | scriptpath = os.path.dirname(os.path.realpath(__file__)) 39 | wasm_fn = os.path.join(scriptpath, f"./wasm/wasmerboy.wasm") 40 | 41 | # Prepare Wasm3 engine 42 | 43 | env = wasm3.Environment() 44 | rt = env.new_runtime(16*1024) 45 | with open(wasm_fn, "rb") as f: 46 | mod = env.parse_module(f.read()) 47 | rt.load(mod) 48 | 49 | # Prepare PyGame 50 | 51 | img_size = (160, 144) 52 | (img_w, img_h) = img_size 53 | scr_size = (img_w*4, img_h*4) 54 | pygame.init() 55 | surface = pygame.display.set_mode(scr_size) 56 | pygame.display.set_caption("Wasm3 WasmBoy") 57 | clock = pygame.time.Clock() 58 | 59 | # WASI emulation 60 | 61 | class FileType: 62 | DIR = 3 63 | REG = 4 64 | 65 | class WasiErrno: 66 | SUCCESS = 0 67 | BADF = 8 68 | INVAL = 28 69 | 70 | def virtual_rom_read(size): 71 | data = rom_f.read(size) 72 | return data 73 | 74 | def virtual_size_write(data): 75 | # Always 160x144 76 | pass 77 | 78 | def virtual_draw_write(data): 79 | global img 80 | img_scaled = pygame.transform.scale(img, scr_size) 81 | surface.blit(img_scaled, (0, 0)) 82 | pygame.display.flip() 83 | 84 | def virtual_fb_write(data): 85 | global img 86 | arr = numpy.frombuffer(data, dtype=numpy.uint8) 87 | if rom_is_color: 88 | # Convert BGRX to RGBX 89 | arr = arr.reshape((img_w*img_h, 4))[..., [2,1,0,3]] 90 | else: 91 | # Convert grayscale to "LCD green shades" 92 | hi = numpy.array([0.659, 0.776, 0.306, 0]) 93 | lo = numpy.array([0.118, 0.129, 0.086, 0]) 94 | w = numpy.array([255, 255, 255, 0]) 95 | arr = arr.reshape((img_w*img_h, 4)) 96 | arr = arr * hi + (w-arr) * lo 97 | data = arr.astype(numpy.uint8).tobytes() 98 | img = pygame.image.frombuffer(data, img_size, "RGBX") 99 | 100 | def virtual_input_read(size): 101 | inputs = b'' 102 | for event in pygame.event.get(): 103 | if (event.type == pygame.QUIT or 104 | (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE)): 105 | pygame.quit() 106 | sys.exit() 107 | elif event.type == pygame.KEYDOWN or event.type == pygame.KEYUP: 108 | keymap = { 109 | pygame.K_UP: 38, 110 | pygame.K_DOWN: 40, 111 | pygame.K_LEFT: 37, 112 | pygame.K_RIGHT: 39, 113 | pygame.K_RETURN: 13, 114 | pygame.K_BACKSPACE: 8, 115 | } 116 | key = keymap.get(event.key, 0) 117 | 118 | if event.type == pygame.KEYDOWN: 119 | inputs += struct.pack(" {fd_val}") 181 | return WasiErrno.SUCCESS 182 | 183 | @wasi_generic_api 184 | def fd_seek(fd, offset, whence, result): 185 | mem = rt.get_memory(0) 186 | #print("fd_seek:", f"{fd} {FilePos(whence)}:{offset}") 187 | struct.pack_into(" d_m3MaxSaneFunctionArgRetCount); 47 | 48 | _ (AllocFuncType (& funcType, (u32) maxNumTypes)); 49 | 50 | u8 * typelist = funcType->types; 51 | 52 | bool parsingRets = true; 53 | while (* sig) 54 | { 55 | char typeChar = * sig++; 56 | 57 | if (typeChar == '(') 58 | { 59 | parsingRets = false; 60 | continue; 61 | } 62 | else if ( typeChar == ' ') 63 | continue; 64 | else if (typeChar == ')') 65 | break; 66 | 67 | u8 type = ConvertTypeCharToTypeId (typeChar); 68 | 69 | _throwif ("unknown argument type char", c_m3Type_unknown == type); 70 | 71 | if (type == c_m3Type_none) 72 | continue; 73 | 74 | if (parsingRets) 75 | { 76 | _throwif ("malformed signature; return count overflow", funcType->numRets >= maxNumTypes); 77 | funcType->numRets++; 78 | *typelist++ = type; 79 | } 80 | else 81 | { 82 | _throwif ("malformed signature; arg count overflow", (u32)(funcType->numRets) + funcType->numArgs >= maxNumTypes); 83 | funcType->numArgs++; 84 | *typelist++ = type; 85 | } 86 | } 87 | 88 | } _catch: 89 | 90 | if (result) 91 | m3_Free (funcType); 92 | 93 | * o_functionType = funcType; 94 | 95 | return result; 96 | } 97 | 98 | 99 | static 100 | M3Result ValidateSignature (IM3Function i_function, ccstr_t i_linkingSignature) 101 | { 102 | M3Result result = m3Err_none; 103 | 104 | IM3FuncType ftype = NULL; 105 | _ (SignatureToFuncType (& ftype, i_linkingSignature)); 106 | 107 | if (not AreFuncTypesEqual (ftype, i_function->funcType)) 108 | { 109 | m3log (module, "expected: %s", SPrintFuncTypeSignature (ftype)); 110 | m3log (module, " found: %s", SPrintFuncTypeSignature (i_function->funcType)); 111 | 112 | _throw ("function signature mismatch"); 113 | } 114 | 115 | _catch: 116 | 117 | m3_Free (ftype); 118 | 119 | return result; 120 | } 121 | 122 | 123 | M3Result FindAndLinkFunction (IM3Module io_module, 124 | ccstr_t i_moduleName, 125 | ccstr_t i_functionName, 126 | ccstr_t i_signature, 127 | voidptr_t i_function, 128 | voidptr_t i_userdata) 129 | { 130 | _try { 131 | _throwif(m3Err_moduleNotLinked, !io_module->runtime); 132 | 133 | const bool wildcardModule = (strcmp (i_moduleName, "*") == 0); 134 | 135 | result = m3Err_functionLookupFailed; 136 | 137 | for (u32 i = 0; i < io_module->numFunctions; ++i) 138 | { 139 | const IM3Function f = & io_module->functions [i]; 140 | 141 | if (f->import.moduleUtf8 and f->import.fieldUtf8) 142 | { 143 | if (strcmp (f->import.fieldUtf8, i_functionName) == 0 and 144 | (wildcardModule or strcmp (f->import.moduleUtf8, i_moduleName) == 0)) 145 | { 146 | if (i_signature) { 147 | _ (ValidateSignature (f, i_signature)); 148 | } 149 | _ (CompileRawFunction (io_module, f, i_function, i_userdata)); 150 | } 151 | } 152 | } 153 | } _catch: 154 | return result; 155 | } 156 | 157 | M3Result m3_LinkRawFunctionEx (IM3Module io_module, 158 | const char * const i_moduleName, 159 | const char * const i_functionName, 160 | const char * const i_signature, 161 | M3RawCall i_function, 162 | const void * i_userdata) 163 | { 164 | return FindAndLinkFunction (io_module, i_moduleName, i_functionName, i_signature, (voidptr_t)i_function, i_userdata); 165 | } 166 | 167 | M3Result m3_LinkRawFunction (IM3Module io_module, 168 | const char * const i_moduleName, 169 | const char * const i_functionName, 170 | const char * const i_signature, 171 | M3RawCall i_function) 172 | { 173 | return FindAndLinkFunction (io_module, i_moduleName, i_functionName, i_signature, (voidptr_t)i_function, NULL); 174 | } 175 | 176 | -------------------------------------------------------------------------------- /wasm3/m3_bind.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_bind.h 3 | // 4 | // Created by Steven Massey on 2/27/20. 5 | // Copyright © 2020 Steven Massey. All rights reserved. 6 | // 7 | 8 | #ifndef m3_bind_h 9 | #define m3_bind_h 10 | 11 | #include "m3_env.h" 12 | 13 | d_m3BeginExternC 14 | 15 | u8 ConvertTypeCharToTypeId (char i_code); 16 | M3Result SignatureToFuncType (IM3FuncType * o_functionType, ccstr_t i_signature); 17 | 18 | d_m3EndExternC 19 | 20 | #endif /* m3_bind_h */ 21 | -------------------------------------------------------------------------------- /wasm3/m3_code.c: -------------------------------------------------------------------------------- 1 | // 2 | // m3_code.c 3 | // 4 | // Created by Steven Massey on 4/19/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | 8 | #include "m3_code.h" 9 | #include "m3_env.h" 10 | 11 | //--------------------------------------------------------------------------------------------------------------------------------- 12 | 13 | 14 | IM3CodePage NewCodePage (IM3Runtime i_runtime, u32 i_minNumLines) 15 | { 16 | IM3CodePage page; 17 | 18 | u32 pageSize = sizeof (M3CodePageHeader) + sizeof (code_t) * i_minNumLines; 19 | 20 | pageSize = (pageSize + (d_m3CodePageAlignSize-1)) & ~(d_m3CodePageAlignSize-1); // align 21 | page = (IM3CodePage)m3_Malloc ("M3CodePage", pageSize); 22 | 23 | if (page) 24 | { 25 | page->info.sequence = ++i_runtime->newCodePageSequence; 26 | page->info.numLines = (pageSize - sizeof (M3CodePageHeader)) / sizeof (code_t); 27 | 28 | #if d_m3RecordBacktraces 29 | u32 pageSizeBt = sizeof (M3CodeMappingPage) + sizeof (M3CodeMapEntry) * page->info.numLines; 30 | page->info.mapping = (M3CodeMappingPage *)m3_Malloc ("M3CodeMappingPage", pageSizeBt); 31 | 32 | if (page->info.mapping) 33 | { 34 | page->info.mapping->size = 0; 35 | page->info.mapping->capacity = page->info.numLines; 36 | } 37 | else 38 | { 39 | m3_Free (page); 40 | return NULL; 41 | } 42 | page->info.mapping->basePC = GetPageStartPC(page); 43 | #endif // d_m3RecordBacktraces 44 | 45 | m3log (runtime, "new page: %p; seq: %d; bytes: %d; lines: %d", GetPagePC (page), page->info.sequence, pageSize, page->info.numLines); 46 | } 47 | 48 | return page; 49 | } 50 | 51 | 52 | void FreeCodePages (IM3CodePage * io_list) 53 | { 54 | IM3CodePage page = * io_list; 55 | 56 | while (page) 57 | { 58 | m3log (code, "free page: %d; %p; util: %3.1f%%", page->info.sequence, page, 100. * page->info.lineIndex / page->info.numLines); 59 | 60 | IM3CodePage next = page->info.next; 61 | #if d_m3RecordBacktraces 62 | m3_Free (page->info.mapping); 63 | #endif // d_m3RecordBacktraces 64 | m3_Free (page); 65 | page = next; 66 | } 67 | 68 | * io_list = NULL; 69 | } 70 | 71 | 72 | u32 NumFreeLines (IM3CodePage i_page) 73 | { 74 | d_m3Assert (i_page->info.lineIndex <= i_page->info.numLines); 75 | 76 | return i_page->info.numLines - i_page->info.lineIndex; 77 | } 78 | 79 | 80 | void EmitWord_impl (IM3CodePage i_page, void * i_word) 81 | { d_m3Assert (i_page->info.lineIndex+1 <= i_page->info.numLines); 82 | i_page->code [i_page->info.lineIndex++] = i_word; 83 | } 84 | 85 | void EmitWord32 (IM3CodePage i_page, const u32 i_word) 86 | { d_m3Assert (i_page->info.lineIndex+1 <= i_page->info.numLines); 87 | * ((u32 *) & i_page->code [i_page->info.lineIndex++]) = i_word; 88 | } 89 | 90 | void EmitWord64 (IM3CodePage i_page, const u64 i_word) 91 | { 92 | #if M3_SIZEOF_PTR == 4 93 | d_m3Assert (i_page->info.lineIndex+2 <= i_page->info.numLines); 94 | * ((u64 *) & i_page->code [i_page->info.lineIndex]) = i_word; 95 | i_page->info.lineIndex += 2; 96 | #else 97 | d_m3Assert (i_page->info.lineIndex+1 <= i_page->info.numLines); 98 | * ((u64 *) & i_page->code [i_page->info.lineIndex]) = i_word; 99 | i_page->info.lineIndex += 1; 100 | #endif 101 | } 102 | 103 | 104 | #if d_m3RecordBacktraces 105 | void EmitMappingEntry (IM3CodePage i_page, u32 i_moduleOffset) 106 | { 107 | M3CodeMappingPage * page = i_page->info.mapping; 108 | d_m3Assert (page->size < page->capacity); 109 | 110 | M3CodeMapEntry * entry = & page->entries[page->size++]; 111 | pc_t pc = GetPagePC (i_page); 112 | 113 | entry->pcOffset = pc - page->basePC; 114 | entry->moduleOffset = i_moduleOffset; 115 | } 116 | #endif // d_m3RecordBacktraces 117 | 118 | pc_t GetPageStartPC (IM3CodePage i_page) 119 | { 120 | return & i_page->code [0]; 121 | } 122 | 123 | 124 | pc_t GetPagePC (IM3CodePage i_page) 125 | { 126 | if (i_page) 127 | return & i_page->code [i_page->info.lineIndex]; 128 | else 129 | return NULL; 130 | } 131 | 132 | 133 | void PushCodePage (IM3CodePage * i_list, IM3CodePage i_codePage) 134 | { 135 | IM3CodePage next = * i_list; 136 | i_codePage->info.next = next; 137 | * i_list = i_codePage; 138 | } 139 | 140 | 141 | IM3CodePage PopCodePage (IM3CodePage * i_list) 142 | { 143 | IM3CodePage page = * i_list; 144 | * i_list = page->info.next; 145 | page->info.next = NULL; 146 | 147 | return page; 148 | } 149 | 150 | 151 | 152 | u32 FindCodePageEnd (IM3CodePage i_list, IM3CodePage * o_end) 153 | { 154 | u32 numPages = 0; 155 | * o_end = NULL; 156 | 157 | while (i_list) 158 | { 159 | * o_end = i_list; 160 | ++numPages; 161 | i_list = i_list->info.next; 162 | } 163 | 164 | return numPages; 165 | } 166 | 167 | 168 | u32 CountCodePages (IM3CodePage i_list) 169 | { 170 | IM3CodePage unused; 171 | return FindCodePageEnd (i_list, & unused); 172 | } 173 | 174 | 175 | IM3CodePage GetEndCodePage (IM3CodePage i_list) 176 | { 177 | IM3CodePage end; 178 | FindCodePageEnd (i_list, & end); 179 | 180 | return end; 181 | } 182 | 183 | #if d_m3RecordBacktraces 184 | bool ContainsPC (IM3CodePage i_page, pc_t i_pc) 185 | { 186 | return GetPageStartPC (i_page) <= i_pc && i_pc < GetPagePC (i_page); 187 | } 188 | 189 | 190 | bool MapPCToOffset (IM3CodePage i_page, pc_t i_pc, u32 * o_moduleOffset) 191 | { 192 | M3CodeMappingPage * mapping = i_page->info.mapping; 193 | 194 | u32 pcOffset = i_pc - mapping->basePC; 195 | 196 | u32 left = 0; 197 | u32 right = mapping->size; 198 | 199 | while (left < right) 200 | { 201 | u32 mid = left + (right - left) / 2; 202 | 203 | if (mapping->entries[mid].pcOffset < pcOffset) 204 | { 205 | left = mid + 1; 206 | } 207 | else if (mapping->entries[mid].pcOffset > pcOffset) 208 | { 209 | right = mid; 210 | } 211 | else 212 | { 213 | *o_moduleOffset = mapping->entries[mid].moduleOffset; 214 | return true; 215 | } 216 | } 217 | 218 | // Getting here means left is now one more than the element we want. 219 | if (left > 0) 220 | { 221 | left--; 222 | *o_moduleOffset = mapping->entries[left].moduleOffset; 223 | return true; 224 | } 225 | else return false; 226 | } 227 | #endif // d_m3RecordBacktraces 228 | 229 | //--------------------------------------------------------------------------------------------------------------------------------- 230 | 231 | 232 | -------------------------------------------------------------------------------- /wasm3/m3_code.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_code.h 3 | // 4 | // Created by Steven Massey on 4/19/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | 8 | #ifndef m3_code_h 9 | #define m3_code_h 10 | 11 | #include "m3_core.h" 12 | 13 | d_m3BeginExternC 14 | 15 | typedef struct M3CodePage 16 | { 17 | M3CodePageHeader info; 18 | code_t code [1]; 19 | } 20 | M3CodePage; 21 | 22 | typedef M3CodePage * IM3CodePage; 23 | 24 | 25 | IM3CodePage NewCodePage (IM3Runtime i_runtime, u32 i_minNumLines); 26 | 27 | void FreeCodePages (IM3CodePage * io_list); 28 | 29 | u32 NumFreeLines (IM3CodePage i_page); 30 | pc_t GetPageStartPC (IM3CodePage i_page); 31 | pc_t GetPagePC (IM3CodePage i_page); 32 | void EmitWord_impl (IM3CodePage i_page, void* i_word); 33 | void EmitWord32 (IM3CodePage i_page, u32 i_word); 34 | void EmitWord64 (IM3CodePage i_page, u64 i_word); 35 | # if d_m3RecordBacktraces 36 | void EmitMappingEntry (IM3CodePage i_page, u32 i_moduleOffset); 37 | # endif // d_m3RecordBacktraces 38 | 39 | void PushCodePage (IM3CodePage * io_list, IM3CodePage i_codePage); 40 | IM3CodePage PopCodePage (IM3CodePage * io_list); 41 | 42 | IM3CodePage GetEndCodePage (IM3CodePage i_list); // i_list = NULL is valid 43 | u32 CountCodePages (IM3CodePage i_list); // i_list = NULL is valid 44 | 45 | # if d_m3RecordBacktraces 46 | bool ContainsPC (IM3CodePage i_page, pc_t i_pc); 47 | bool MapPCToOffset (IM3CodePage i_page, pc_t i_pc, u32 * o_moduleOffset); 48 | # endif // d_m3RecordBacktraces 49 | 50 | # ifdef DEBUG 51 | void dump_code_page (IM3CodePage i_codePage, pc_t i_startPC); 52 | # endif 53 | 54 | #define EmitWord(page, val) EmitWord_impl(page, (void*)(val)) 55 | 56 | //--------------------------------------------------------------------------------------------------------------------------------- 57 | 58 | # if d_m3RecordBacktraces 59 | 60 | typedef struct M3CodeMapEntry 61 | { 62 | u32 pcOffset; 63 | u32 moduleOffset; 64 | } 65 | M3CodeMapEntry; 66 | 67 | typedef struct M3CodeMappingPage 68 | { 69 | pc_t basePC; 70 | u32 size; 71 | u32 capacity; 72 | M3CodeMapEntry entries []; 73 | } 74 | M3CodeMappingPage; 75 | 76 | # endif // d_m3RecordBacktraces 77 | 78 | d_m3EndExternC 79 | 80 | #endif // m3_code_h 81 | -------------------------------------------------------------------------------- /wasm3/m3_compile.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_compile.h 3 | // 4 | // Created by Steven Massey on 4/17/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | 8 | #ifndef m3_compile_h 9 | #define m3_compile_h 10 | 11 | #include "m3_code.h" 12 | #include "m3_exec_defs.h" 13 | #include "m3_function.h" 14 | 15 | d_m3BeginExternC 16 | 17 | enum 18 | { 19 | c_waOp_block = 0x02, 20 | c_waOp_loop = 0x03, 21 | c_waOp_if = 0x04, 22 | c_waOp_else = 0x05, 23 | c_waOp_end = 0x0b, 24 | c_waOp_branch = 0x0c, 25 | c_waOp_branchTable = 0x0e, 26 | c_waOp_branchIf = 0x0d, 27 | c_waOp_call = 0x10, 28 | c_waOp_getLocal = 0x20, 29 | c_waOp_setLocal = 0x21, 30 | c_waOp_teeLocal = 0x22, 31 | 32 | c_waOp_getGlobal = 0x23, 33 | 34 | c_waOp_store_f32 = 0x38, 35 | c_waOp_store_f64 = 0x39, 36 | 37 | c_waOp_i32_const = 0x41, 38 | c_waOp_i64_const = 0x42, 39 | c_waOp_f32_const = 0x43, 40 | c_waOp_f64_const = 0x44, 41 | 42 | c_waOp_extended = 0xfc, 43 | 44 | c_waOp_memoryCopy = 0xfc0a, 45 | c_waOp_memoryFill = 0xfc0b 46 | }; 47 | 48 | 49 | #define d_FuncRetType(ftype,i) ((ftype)->types[(i)]) 50 | #define d_FuncArgType(ftype,i) ((ftype)->types[(ftype)->numRets + (i)]) 51 | 52 | //----------------------------------------------------------------------------------------------------------------------------------- 53 | 54 | typedef struct M3CompilationScope 55 | { 56 | struct M3CompilationScope * outer; 57 | 58 | pc_t pc; // used by ContinueLoop's 59 | pc_t patches; 60 | i32 depth; 61 | u16 exitStackIndex; 62 | i16 blockStackIndex; 63 | // u16 topSlot; 64 | IM3FuncType type; 65 | m3opcode_t opcode; 66 | bool isPolymorphic; 67 | } 68 | M3CompilationScope; 69 | 70 | typedef M3CompilationScope * IM3CompilationScope; 71 | 72 | typedef struct 73 | { 74 | IM3Runtime runtime; 75 | IM3Module module; 76 | 77 | bytes_t wasm; 78 | bytes_t wasmEnd; 79 | bytes_t lastOpcodeStart; 80 | 81 | M3CompilationScope block; 82 | 83 | IM3Function function; 84 | 85 | IM3CodePage page; 86 | 87 | #ifdef DEBUG 88 | u32 numEmits; 89 | u32 numOpcodes; 90 | #endif 91 | 92 | u16 stackFirstDynamicIndex; // args and locals are pushed to the stack so that their slot locations can be tracked. the wasm model itself doesn't 93 | // treat these values as being on the stack, so stackFirstDynamicIndex marks the start of the real Wasm stack 94 | u16 stackIndex; // current stack top 95 | 96 | u16 slotFirstConstIndex; 97 | u16 slotMaxConstIndex; // as const's are encountered during compilation this tracks their location in the "real" stack 98 | 99 | u16 slotFirstLocalIndex; 100 | u16 slotFirstDynamicIndex; // numArgs + numLocals + numReservedConstants. the first mutable slot available to the compiler. 101 | 102 | u16 maxStackSlots; 103 | 104 | m3slot_t constants [d_m3MaxConstantTableSize]; 105 | 106 | // 'wasmStack' holds slot locations 107 | u16 wasmStack [d_m3MaxFunctionStackHeight]; 108 | u8 typeStack [d_m3MaxFunctionStackHeight]; 109 | 110 | // 'm3Slots' contains allocation usage counts 111 | u8 m3Slots [d_m3MaxFunctionSlots]; 112 | 113 | u16 slotMaxAllocatedIndexPlusOne; 114 | 115 | u16 regStackIndexPlusOne [2]; 116 | 117 | m3opcode_t previousOpcode; 118 | } 119 | M3Compilation; 120 | 121 | typedef M3Compilation * IM3Compilation; 122 | 123 | typedef M3Result (* M3Compiler) (IM3Compilation, m3opcode_t); 124 | 125 | 126 | //----------------------------------------------------------------------------------------------------------------------------------- 127 | 128 | 129 | typedef struct M3OpInfo 130 | { 131 | #ifdef DEBUG 132 | const char * const name; 133 | #endif 134 | 135 | i8 stackOffset; 136 | u8 type; 137 | 138 | // for most operations: 139 | // [0]= top operand in register, [1]= top operand in stack, [2]= both operands in stack 140 | IM3Operation operations [4]; 141 | 142 | M3Compiler compiler; 143 | } 144 | M3OpInfo; 145 | 146 | typedef const M3OpInfo * IM3OpInfo; 147 | 148 | IM3OpInfo GetOpInfo (m3opcode_t opcode); 149 | 150 | // TODO: This helper should be removed, when MultiValue is implemented 151 | static inline 152 | u8 GetSingleRetType(IM3FuncType ftype) { 153 | return (ftype && ftype->numRets) ? ftype->types[0] : (u8)c_m3Type_none; 154 | } 155 | 156 | static const u16 c_m3RegisterUnallocated = 0; 157 | static const u16 c_slotUnused = 0xffff; 158 | 159 | static inline 160 | bool IsRegisterAllocated (IM3Compilation o, u32 i_register) 161 | { 162 | return (o->regStackIndexPlusOne [i_register] != c_m3RegisterUnallocated); 163 | } 164 | 165 | static inline 166 | bool IsStackPolymorphic (IM3Compilation o) 167 | { 168 | return o->block.isPolymorphic; 169 | } 170 | 171 | static inline bool IsRegisterSlotAlias (u16 i_slot) { return (i_slot >= d_m3Reg0SlotAlias and i_slot != c_slotUnused); } 172 | static inline bool IsFpRegisterSlotAlias (u16 i_slot) { return (i_slot == d_m3Fp0SlotAlias); } 173 | static inline bool IsIntRegisterSlotAlias (u16 i_slot) { return (i_slot == d_m3Reg0SlotAlias); } 174 | 175 | 176 | #ifdef DEBUG 177 | #define M3OP(...) { __VA_ARGS__ } 178 | #define M3OP_RESERVED { "reserved" } 179 | #else 180 | // Strip-off name 181 | #define M3OP(name, ...) { __VA_ARGS__ } 182 | #define M3OP_RESERVED { 0 } 183 | #endif 184 | 185 | #if d_m3HasFloat 186 | #define M3OP_F M3OP 187 | #elif d_m3NoFloatDynamic 188 | #define M3OP_F(n,o,t,op,...) M3OP(n, o, t, { op_Unsupported, op_Unsupported, op_Unsupported, op_Unsupported }, __VA_ARGS__) 189 | #else 190 | #define M3OP_F(...) { 0 } 191 | #endif 192 | 193 | //----------------------------------------------------------------------------------------------------------------------------------- 194 | 195 | u16 GetMaxUsedSlotPlusOne (IM3Compilation o); 196 | 197 | M3Result CompileBlock (IM3Compilation io, IM3FuncType i_blockType, m3opcode_t i_blockOpcode); 198 | 199 | M3Result CompileBlockStatements (IM3Compilation io); 200 | M3Result CompileFunction (IM3Function io_function); 201 | 202 | M3Result CompileRawFunction (IM3Module io_module, IM3Function io_function, const void * i_function, const void * i_userdata); 203 | 204 | d_m3EndExternC 205 | 206 | #endif // m3_compile_h 207 | -------------------------------------------------------------------------------- /wasm3/m3_config.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_config.h 3 | // 4 | // Created by Steven Massey on 5/4/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | 8 | #ifndef m3_config_h 9 | #define m3_config_h 10 | 11 | #include "m3_config_platforms.h" 12 | 13 | // general -------------------------------------------------------------------- 14 | 15 | # ifndef d_m3CodePageAlignSize 16 | # define d_m3CodePageAlignSize 32*1024 17 | # endif 18 | 19 | # ifndef d_m3MaxFunctionStackHeight 20 | # define d_m3MaxFunctionStackHeight 16384 // max: 32768 21 | # endif 22 | 23 | # ifndef d_m3MaxLinearMemoryPages 24 | # define d_m3MaxLinearMemoryPages 65536 25 | # endif 26 | 27 | # ifndef d_m3MaxFunctionSlots 28 | # define d_m3MaxFunctionSlots ((d_m3MaxFunctionStackHeight)*2) 29 | # endif 30 | 31 | # ifndef d_m3MaxConstantTableSize 32 | # define d_m3MaxConstantTableSize 120 33 | # endif 34 | 35 | # ifndef d_m3MaxDuplicateFunctionImpl 36 | # define d_m3MaxDuplicateFunctionImpl 3 37 | # endif 38 | 39 | # ifndef d_m3CascadedOpcodes // Cascaded opcodes are slightly faster at the expense of some memory 40 | # define d_m3CascadedOpcodes 1 // Adds ~3Kb to operations table in m3_compile.c 41 | # endif 42 | 43 | # ifndef d_m3VerboseErrorMessages 44 | # define d_m3VerboseErrorMessages 1 45 | # endif 46 | 47 | # ifndef d_m3FixedHeap 48 | # define d_m3FixedHeap false 49 | //# define d_m3FixedHeap (32*1024) 50 | # endif 51 | 52 | # ifndef d_m3FixedHeapAlign 53 | # define d_m3FixedHeapAlign 16 54 | # endif 55 | 56 | # ifndef d_m3Use32BitSlots 57 | # define d_m3Use32BitSlots 1 58 | # endif 59 | 60 | # ifndef d_m3ProfilerSlotMask 61 | # define d_m3ProfilerSlotMask 0xFFFF 62 | # endif 63 | 64 | # ifndef d_m3RecordBacktraces 65 | # define d_m3RecordBacktraces 0 66 | # endif 67 | 68 | # ifndef d_m3EnableExceptionBreakpoint 69 | # define d_m3EnableExceptionBreakpoint 0 // see m3_exception.h 70 | # endif 71 | 72 | 73 | // profiling and tracing ------------------------------------------------------ 74 | 75 | # ifndef d_m3EnableOpProfiling 76 | # define d_m3EnableOpProfiling 0 // opcode usage counters 77 | # endif 78 | 79 | # ifndef d_m3EnableOpTracing 80 | # define d_m3EnableOpTracing 0 // only works with DEBUG 81 | # endif 82 | 83 | # ifndef d_m3EnableStrace 84 | # define d_m3EnableStrace 0 // 1 - trace exported function calls 85 | // 2 - trace all calls (structured) - requires DEBUG 86 | // 3 - all calls + loops + memory operations 87 | # endif 88 | 89 | 90 | // logging -------------------------------------------------------------------- 91 | 92 | # ifndef d_m3LogParse 93 | # define d_m3LogParse 0 // .wasm binary decoding info 94 | # endif 95 | 96 | # ifndef d_m3LogModule 97 | # define d_m3LogModule 0 // wasm module info 98 | # endif 99 | 100 | # ifndef d_m3LogCompile 101 | # define d_m3LogCompile 0 // wasm -> metacode generation phase 102 | # endif 103 | 104 | # ifndef d_m3LogWasmStack 105 | # define d_m3LogWasmStack 0 // dump the wasm stack when pushed or popped 106 | # endif 107 | 108 | # ifndef d_m3LogEmit 109 | # define d_m3LogEmit 0 // metacode generation info 110 | # endif 111 | 112 | # ifndef d_m3LogCodePages 113 | # define d_m3LogCodePages 0 // dump metacode pages when released 114 | # endif 115 | 116 | # ifndef d_m3LogRuntime 117 | # define d_m3LogRuntime 0 // higher-level runtime information 118 | # endif 119 | 120 | # ifndef d_m3LogNativeStack 121 | # define d_m3LogNativeStack 0 // track the memory usage of the C-stack 122 | # endif 123 | 124 | # ifndef d_m3LogHeapOps 125 | # define d_m3LogHeapOps 0 // track heap usage 126 | # endif 127 | 128 | # ifndef d_m3LogTimestamps 129 | # define d_m3LogTimestamps 0 // track timestamps on heap logs 130 | # endif 131 | 132 | // other ---------------------------------------------------------------------- 133 | 134 | # ifndef d_m3HasFloat 135 | # define d_m3HasFloat 1 // implement floating point ops 136 | # endif 137 | 138 | #if !d_m3HasFloat && !defined(d_m3NoFloatDynamic) 139 | # define d_m3NoFloatDynamic 1 // if no floats, do not fail until flops are actually executed 140 | #endif 141 | 142 | # ifndef d_m3SkipStackCheck 143 | # define d_m3SkipStackCheck 0 // skip stack overrun checks 144 | # endif 145 | 146 | # ifndef d_m3SkipMemoryBoundsCheck 147 | # define d_m3SkipMemoryBoundsCheck 0 // skip memory bounds checks 148 | # endif 149 | 150 | #endif // m3_config_h 151 | -------------------------------------------------------------------------------- /wasm3/m3_config_platforms.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_config_platforms.h 3 | // 4 | // Created by Volodymyr Shymanskyy on 11/20/19. 5 | // Copyright © 2019 Volodymyr Shymanskyy. All rights reserved. 6 | // 7 | 8 | #ifndef m3_config_platforms_h 9 | #define m3_config_platforms_h 10 | 11 | #include "wasm3_defs.h" 12 | 13 | /* 14 | * Internal helpers 15 | */ 16 | 17 | # if !defined(__cplusplus) || defined(_MSC_VER) 18 | # define not ! 19 | # define and && 20 | # define or || 21 | # endif 22 | 23 | /* 24 | * Detect/define features 25 | */ 26 | 27 | # if defined(M3_COMPILER_MSVC) 28 | # include 29 | # if UINTPTR_MAX == 0xFFFFFFFF 30 | # define M3_SIZEOF_PTR 4 31 | # elif UINTPTR_MAX == 0xFFFFFFFFFFFFFFFFu 32 | # define M3_SIZEOF_PTR 8 33 | # else 34 | # error "Pointer size not supported" 35 | # endif 36 | # elif defined(__SIZEOF_POINTER__) 37 | # define M3_SIZEOF_PTR __SIZEOF_POINTER__ 38 | #else 39 | # error "Pointer size not detected" 40 | # endif 41 | 42 | # if defined(M3_BIG_ENDIAN) 43 | # define M3_BSWAP_u8(X) {} 44 | # define M3_BSWAP_u16(X) { (X)=m3_bswap16((X)); } 45 | # define M3_BSWAP_u32(X) { (X)=m3_bswap32((X)); } 46 | # define M3_BSWAP_u64(X) { (X)=m3_bswap64((X)); } 47 | # define M3_BSWAP_i8(X) {} 48 | # define M3_BSWAP_i16(X) M3_BSWAP_u16(X) 49 | # define M3_BSWAP_i32(X) M3_BSWAP_u32(X) 50 | # define M3_BSWAP_i64(X) M3_BSWAP_u64(X) 51 | # define M3_BSWAP_f32(X) { union { f32 f; u32 i; } u; u.f = (X); M3_BSWAP_u32(u.i); (X) = u.f; } 52 | # define M3_BSWAP_f64(X) { union { f64 f; u64 i; } u; u.f = (X); M3_BSWAP_u64(u.i); (X) = u.f; } 53 | # else 54 | # define M3_BSWAP_u8(X) {} 55 | # define M3_BSWAP_u16(x) {} 56 | # define M3_BSWAP_u32(x) {} 57 | # define M3_BSWAP_u64(x) {} 58 | # define M3_BSWAP_i8(X) {} 59 | # define M3_BSWAP_i16(X) {} 60 | # define M3_BSWAP_i32(X) {} 61 | # define M3_BSWAP_i64(X) {} 62 | # define M3_BSWAP_f32(X) {} 63 | # define M3_BSWAP_f64(X) {} 64 | # endif 65 | 66 | # if defined(M3_COMPILER_MSVC) 67 | # define M3_WEAK //__declspec(selectany) 68 | # define M3_NO_UBSAN 69 | # define M3_NOINLINE 70 | # elif defined(__MINGW32__) || defined(__CYGWIN__) 71 | # define M3_WEAK //__attribute__((selectany)) 72 | # define M3_NO_UBSAN 73 | # define M3_NOINLINE __attribute__((noinline)) 74 | # else 75 | # define M3_WEAK __attribute__((weak)) 76 | # define M3_NO_UBSAN //__attribute__((no_sanitize("undefined"))) 77 | // Workaround for Cosmopolitan noinline conflict: https://github.com/jart/cosmopolitan/issues/310 78 | # if defined(noinline) 79 | # define M3_NOINLINE noinline 80 | # else 81 | # define M3_NOINLINE __attribute__((noinline)) 82 | # endif 83 | # endif 84 | 85 | # ifndef M3_MIN 86 | # define M3_MIN(A,B) (((A) < (B)) ? (A) : (B)) 87 | # endif 88 | # ifndef M3_MAX 89 | # define M3_MAX(A,B) (((A) > (B)) ? (A) : (B)) 90 | # endif 91 | 92 | #define M3_INIT(field) memset(&field, 0, sizeof(field)) 93 | 94 | #define M3_COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x]))))) 95 | 96 | #if defined(__AVR__) 97 | 98 | #include 99 | 100 | # define PRIu64 "llu" 101 | # define PRIi64 "lli" 102 | 103 | # define d_m3ShortTypesDefined 104 | typedef double f64; 105 | typedef float f32; 106 | typedef uint64_t u64; 107 | typedef int64_t i64; 108 | typedef uint32_t u32; 109 | typedef int32_t i32; 110 | typedef short unsigned u16; 111 | typedef short i16; 112 | typedef uint8_t u8; 113 | typedef int8_t i8; 114 | 115 | #endif 116 | 117 | /* 118 | * Apply settings 119 | */ 120 | 121 | # if defined (M3_COMPILER_MSVC) 122 | # define vectorcall // For MSVC, better not to specify any call convention 123 | # elif defined(__x86_64__) 124 | # define vectorcall __attribute__((aligned(32))) 125 | //# elif defined(__riscv) && (__riscv_xlen == 64) 126 | //# define vectorcall __attribute__((aligned(16))) 127 | # elif defined(__MINGW32__) 128 | # define vectorcall 129 | # elif defined(WIN32) 130 | # define vectorcall __vectorcall 131 | # elif defined (ESP8266) 132 | # include 133 | # define vectorcall //ICACHE_FLASH_ATTR 134 | # elif defined (ESP32) 135 | # if defined(M3_IN_IRAM) // the interpreter is in IRAM, attribute not needed 136 | # define vectorcall 137 | # else 138 | # include "esp_system.h" 139 | # define vectorcall IRAM_ATTR 140 | # endif 141 | # elif defined (FOMU) 142 | # define vectorcall __attribute__((section(".ramtext"))) 143 | # endif 144 | 145 | #ifndef vectorcall 146 | #define vectorcall 147 | #endif 148 | 149 | 150 | /* 151 | * Device-specific defaults 152 | */ 153 | 154 | # ifndef d_m3MaxFunctionStackHeight 155 | # if defined(ESP8266) || defined(ESP32) || defined(ARDUINO_AMEBA) || defined(TEENSYDUINO) 156 | # define d_m3MaxFunctionStackHeight 128 157 | # endif 158 | # endif 159 | 160 | # ifndef d_m3FixedHeap 161 | # if defined(ARDUINO_AMEBA) 162 | # define d_m3FixedHeap (128*1024) 163 | # elif defined(BLUE_PILL) || defined(FOMU) 164 | # define d_m3FixedHeap (12*1024) 165 | # elif defined(ARDUINO_ARCH_ARC32) // Arduino 101 166 | # define d_m3FixedHeap (10*1024) 167 | # endif 168 | # endif 169 | 170 | /* 171 | * Platform-specific defaults 172 | */ 173 | 174 | # if defined(ARDUINO) || defined(PARTICLE) || defined(PLATFORMIO) || defined(__MBED__) || \ 175 | defined(ESP8266) || defined(ESP32) || defined(BLUE_PILL) || defined(WM_W600) || defined(FOMU) 176 | # ifndef d_m3CascadedOpcodes 177 | # define d_m3CascadedOpcodes 0 178 | # endif 179 | # ifndef d_m3VerboseErrorMessages 180 | # define d_m3VerboseErrorMessages 0 181 | # endif 182 | # ifndef d_m3MaxConstantTableSize 183 | # define d_m3MaxConstantTableSize 64 184 | # endif 185 | # ifndef d_m3MaxFunctionStackHeight 186 | # define d_m3MaxFunctionStackHeight 64 187 | # endif 188 | # ifndef d_m3CodePageAlignSize 189 | # define d_m3CodePageAlignSize 1024 190 | # endif 191 | # endif 192 | 193 | /* 194 | * Arch-specific defaults 195 | */ 196 | #if defined(__riscv) && (__riscv_xlen == 64) 197 | # ifndef d_m3Use32BitSlots 198 | # define d_m3Use32BitSlots 0 199 | # endif 200 | #endif 201 | 202 | #endif // m3_config_platforms_h 203 | -------------------------------------------------------------------------------- /wasm3/m3_core.c: -------------------------------------------------------------------------------- 1 | // 2 | // m3_core.c 3 | // 4 | // Created by Steven Massey on 4/15/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | 8 | #define M3_IMPLEMENT_ERROR_STRINGS 9 | #include "m3_config.h" 10 | #include "wasm3.h" 11 | 12 | #include "m3_core.h" 13 | #include "m3_env.h" 14 | 15 | void m3_Abort(const char* message) { 16 | #ifdef DEBUG 17 | fprintf(stderr, "Error: %s\n", message); 18 | #endif 19 | abort(); 20 | } 21 | 22 | M3_WEAK 23 | M3Result m3_Yield () 24 | { 25 | return m3Err_none; 26 | } 27 | 28 | #if d_m3LogTimestamps 29 | 30 | #include 31 | 32 | #define SEC_TO_US(sec) ((sec)*1000000) 33 | #define NS_TO_US(ns) ((ns)/1000) 34 | 35 | static uint64_t initial_ts = -1; 36 | 37 | uint64_t m3_GetTimestamp() 38 | { 39 | if (initial_ts == -1) { 40 | initial_ts = 0; 41 | initial_ts = m3_GetTimestamp(); 42 | } 43 | struct timespec ts; 44 | timespec_get(&ts, TIME_UTC); 45 | uint64_t us = SEC_TO_US((uint64_t)ts.tv_sec) + NS_TO_US((uint64_t)ts.tv_nsec); 46 | return us - initial_ts; 47 | } 48 | 49 | #endif 50 | 51 | #if d_m3FixedHeap 52 | 53 | static u8 fixedHeap[d_m3FixedHeap]; 54 | static u8* fixedHeapPtr = fixedHeap; 55 | static u8* const fixedHeapEnd = fixedHeap + d_m3FixedHeap; 56 | static u8* fixedHeapLast = NULL; 57 | 58 | #if d_m3FixedHeapAlign > 1 59 | # define HEAP_ALIGN_PTR(P) P = (u8*)(((size_t)(P)+(d_m3FixedHeapAlign-1)) & ~ (d_m3FixedHeapAlign-1)); 60 | #else 61 | # define HEAP_ALIGN_PTR(P) 62 | #endif 63 | 64 | void * m3_Malloc_Impl (size_t i_size) 65 | { 66 | u8 * ptr = fixedHeapPtr; 67 | 68 | fixedHeapPtr += i_size; 69 | HEAP_ALIGN_PTR(fixedHeapPtr); 70 | 71 | if (fixedHeapPtr >= fixedHeapEnd) 72 | { 73 | return NULL; 74 | } 75 | 76 | memset (ptr, 0x0, i_size); 77 | fixedHeapLast = ptr; 78 | 79 | return ptr; 80 | } 81 | 82 | void m3_Free_Impl (void * i_ptr) 83 | { 84 | // Handle the last chunk 85 | if (i_ptr && i_ptr == fixedHeapLast) { 86 | fixedHeapPtr = fixedHeapLast; 87 | fixedHeapLast = NULL; 88 | } else { 89 | //printf("== free %p [failed]\n", io_ptr); 90 | } 91 | } 92 | 93 | void * m3_Realloc_Impl (void * i_ptr, size_t i_newSize, size_t i_oldSize) 94 | { 95 | if (M3_UNLIKELY(i_newSize == i_oldSize)) return i_ptr; 96 | 97 | void * newPtr; 98 | 99 | // Handle the last chunk 100 | if (i_ptr && i_ptr == fixedHeapLast) { 101 | fixedHeapPtr = fixedHeapLast + i_newSize; 102 | HEAP_ALIGN_PTR(fixedHeapPtr); 103 | if (fixedHeapPtr >= fixedHeapEnd) 104 | { 105 | return NULL; 106 | } 107 | newPtr = i_ptr; 108 | } else { 109 | newPtr = m3_Malloc_Impl(i_newSize); 110 | if (!newPtr) { 111 | return NULL; 112 | } 113 | if (i_ptr) { 114 | memcpy(newPtr, i_ptr, i_oldSize); 115 | } 116 | } 117 | 118 | if (i_newSize > i_oldSize) { 119 | memset ((u8 *) newPtr + i_oldSize, 0x0, i_newSize - i_oldSize); 120 | } 121 | 122 | return newPtr; 123 | } 124 | 125 | #else 126 | 127 | void * m3_Malloc_Impl (size_t i_size) 128 | { 129 | return calloc (i_size, 1); 130 | } 131 | 132 | void m3_Free_Impl (void * io_ptr) 133 | { 134 | free (io_ptr); 135 | } 136 | 137 | void * m3_Realloc_Impl (void * i_ptr, size_t i_newSize, size_t i_oldSize) 138 | { 139 | if (M3_UNLIKELY(i_newSize == i_oldSize)) return i_ptr; 140 | 141 | void * newPtr = realloc (i_ptr, i_newSize); 142 | 143 | if (M3_LIKELY(newPtr)) 144 | { 145 | if (i_newSize > i_oldSize) { 146 | memset ((u8 *) newPtr + i_oldSize, 0x0, i_newSize - i_oldSize); 147 | } 148 | return newPtr; 149 | } 150 | return NULL; 151 | } 152 | 153 | #endif 154 | 155 | void * m3_CopyMem (const void * i_from, size_t i_size) 156 | { 157 | void * ptr = m3_Malloc("CopyMem", i_size); 158 | if (ptr) { 159 | memcpy (ptr, i_from, i_size); 160 | } 161 | return ptr; 162 | } 163 | 164 | //-------------------------------------------------------------------------------------------- 165 | 166 | #if d_m3LogNativeStack 167 | 168 | static size_t stack_start; 169 | static size_t stack_end; 170 | 171 | void m3StackCheckInit () 172 | { 173 | char stack; 174 | stack_end = stack_start = (size_t)&stack; 175 | } 176 | 177 | void m3StackCheck () 178 | { 179 | char stack; 180 | size_t addr = (size_t)&stack; 181 | 182 | size_t stackEnd = stack_end; 183 | stack_end = M3_MIN (stack_end, addr); 184 | 185 | // if (stackEnd != stack_end) 186 | // printf ("maxStack: %ld\n", m3StackGetMax ()); 187 | } 188 | 189 | int m3StackGetMax () 190 | { 191 | return stack_start - stack_end; 192 | } 193 | 194 | #endif 195 | 196 | //-------------------------------------------------------------------------------------------- 197 | 198 | M3Result NormalizeType (u8 * o_type, i8 i_convolutedWasmType) 199 | { 200 | M3Result result = m3Err_none; 201 | 202 | u8 type = -i_convolutedWasmType; 203 | 204 | if (type == 0x40) 205 | type = c_m3Type_none; 206 | else if (type < c_m3Type_i32 or type > c_m3Type_f64) 207 | result = m3Err_invalidTypeId; 208 | 209 | * o_type = type; 210 | 211 | return result; 212 | } 213 | 214 | 215 | bool IsFpType (u8 i_m3Type) 216 | { 217 | return (i_m3Type == c_m3Type_f32 or i_m3Type == c_m3Type_f64); 218 | } 219 | 220 | 221 | bool IsIntType (u8 i_m3Type) 222 | { 223 | return (i_m3Type == c_m3Type_i32 or i_m3Type == c_m3Type_i64); 224 | } 225 | 226 | 227 | bool Is64BitType (u8 i_m3Type) 228 | { 229 | if (i_m3Type == c_m3Type_i64 or i_m3Type == c_m3Type_f64) 230 | return true; 231 | else if (i_m3Type == c_m3Type_i32 or i_m3Type == c_m3Type_f32 or i_m3Type == c_m3Type_none) 232 | return false; 233 | else 234 | return (sizeof (voidptr_t) == 8); // all other cases are pointers 235 | } 236 | 237 | u32 SizeOfType (u8 i_m3Type) 238 | { 239 | if (i_m3Type == c_m3Type_i32 or i_m3Type == c_m3Type_f32) 240 | return sizeof (i32); 241 | 242 | return sizeof (i64); 243 | } 244 | 245 | 246 | //-- Binary Wasm parsing utils ------------------------------------------------------------------------------------------ 247 | 248 | 249 | M3Result Read_u64 (u64 * o_value, bytes_t * io_bytes, cbytes_t i_end) 250 | { 251 | const u8 * ptr = * io_bytes; 252 | ptr += sizeof (u64); 253 | 254 | if (ptr <= i_end) 255 | { 256 | memcpy(o_value, * io_bytes, sizeof(u64)); 257 | M3_BSWAP_u64(*o_value); 258 | * io_bytes = ptr; 259 | return m3Err_none; 260 | } 261 | else return m3Err_wasmUnderrun; 262 | } 263 | 264 | 265 | M3Result Read_u32 (u32 * o_value, bytes_t * io_bytes, cbytes_t i_end) 266 | { 267 | const u8 * ptr = * io_bytes; 268 | ptr += sizeof (u32); 269 | 270 | if (ptr <= i_end) 271 | { 272 | memcpy(o_value, * io_bytes, sizeof(u32)); 273 | M3_BSWAP_u32(*o_value); 274 | * io_bytes = ptr; 275 | return m3Err_none; 276 | } 277 | else return m3Err_wasmUnderrun; 278 | } 279 | 280 | #if d_m3ImplementFloat 281 | 282 | M3Result Read_f64 (f64 * o_value, bytes_t * io_bytes, cbytes_t i_end) 283 | { 284 | const u8 * ptr = * io_bytes; 285 | ptr += sizeof (f64); 286 | 287 | if (ptr <= i_end) 288 | { 289 | memcpy(o_value, * io_bytes, sizeof(f64)); 290 | M3_BSWAP_f64(*o_value); 291 | * io_bytes = ptr; 292 | return m3Err_none; 293 | } 294 | else return m3Err_wasmUnderrun; 295 | } 296 | 297 | 298 | M3Result Read_f32 (f32 * o_value, bytes_t * io_bytes, cbytes_t i_end) 299 | { 300 | const u8 * ptr = * io_bytes; 301 | ptr += sizeof (f32); 302 | 303 | if (ptr <= i_end) 304 | { 305 | memcpy(o_value, * io_bytes, sizeof(f32)); 306 | M3_BSWAP_f32(*o_value); 307 | * io_bytes = ptr; 308 | return m3Err_none; 309 | } 310 | else return m3Err_wasmUnderrun; 311 | } 312 | 313 | #endif 314 | 315 | M3Result Read_u8 (u8 * o_value, bytes_t * io_bytes, cbytes_t i_end) 316 | { 317 | const u8 * ptr = * io_bytes; 318 | 319 | if (ptr < i_end) 320 | { 321 | * o_value = * ptr; 322 | * io_bytes = ptr + 1; 323 | 324 | return m3Err_none; 325 | } 326 | else return m3Err_wasmUnderrun; 327 | } 328 | 329 | M3Result Read_opcode (m3opcode_t * o_value, bytes_t * io_bytes, cbytes_t i_end) 330 | { 331 | const u8 * ptr = * io_bytes; 332 | 333 | if (ptr < i_end) 334 | { 335 | m3opcode_t opcode = * ptr++; 336 | 337 | #if d_m3CascadedOpcodes == 0 338 | if (M3_UNLIKELY(opcode == c_waOp_extended)) 339 | { 340 | if (ptr < i_end) 341 | { 342 | opcode = (opcode << 8) | (* ptr++); 343 | } 344 | else return m3Err_wasmUnderrun; 345 | } 346 | #endif 347 | * o_value = opcode; 348 | * io_bytes = ptr; 349 | 350 | return m3Err_none; 351 | } 352 | else return m3Err_wasmUnderrun; 353 | } 354 | 355 | 356 | M3Result ReadLebUnsigned (u64 * o_value, u32 i_maxNumBits, bytes_t * io_bytes, cbytes_t i_end) 357 | { 358 | M3Result result = m3Err_wasmUnderrun; 359 | 360 | u64 value = 0; 361 | 362 | u32 shift = 0; 363 | const u8 * ptr = * io_bytes; 364 | 365 | while (ptr < i_end) 366 | { 367 | u64 byte = * (ptr++); 368 | 369 | value |= ((byte & 0x7f) << shift); 370 | shift += 7; 371 | 372 | if ((byte & 0x80) == 0) 373 | { 374 | result = m3Err_none; 375 | break; 376 | } 377 | 378 | if (shift >= i_maxNumBits) 379 | { 380 | result = m3Err_lebOverflow; 381 | break; 382 | } 383 | } 384 | 385 | * o_value = value; 386 | * io_bytes = ptr; 387 | 388 | return result; 389 | } 390 | 391 | 392 | M3Result ReadLebSigned (i64 * o_value, u32 i_maxNumBits, bytes_t * io_bytes, cbytes_t i_end) 393 | { 394 | M3Result result = m3Err_wasmUnderrun; 395 | 396 | i64 value = 0; 397 | 398 | u32 shift = 0; 399 | const u8 * ptr = * io_bytes; 400 | 401 | while (ptr < i_end) 402 | { 403 | u64 byte = * (ptr++); 404 | 405 | value |= ((byte & 0x7f) << shift); 406 | shift += 7; 407 | 408 | if ((byte & 0x80) == 0) 409 | { 410 | result = m3Err_none; 411 | 412 | if ((byte & 0x40) and (shift < 64)) // do sign extension 413 | { 414 | u64 extend = 0; 415 | value |= (~extend << shift); 416 | } 417 | 418 | break; 419 | } 420 | 421 | if (shift >= i_maxNumBits) 422 | { 423 | result = m3Err_lebOverflow; 424 | break; 425 | } 426 | } 427 | 428 | * o_value = value; 429 | * io_bytes = ptr; 430 | 431 | return result; 432 | } 433 | 434 | 435 | M3Result ReadLEB_u32 (u32 * o_value, bytes_t * io_bytes, cbytes_t i_end) 436 | { 437 | u64 value; 438 | M3Result result = ReadLebUnsigned (& value, 32, io_bytes, i_end); 439 | * o_value = (u32) value; 440 | 441 | return result; 442 | } 443 | 444 | 445 | M3Result ReadLEB_u7 (u8 * o_value, bytes_t * io_bytes, cbytes_t i_end) 446 | { 447 | u64 value; 448 | M3Result result = ReadLebUnsigned (& value, 7, io_bytes, i_end); 449 | * o_value = (u8) value; 450 | 451 | return result; 452 | } 453 | 454 | 455 | M3Result ReadLEB_i7 (i8 * o_value, bytes_t * io_bytes, cbytes_t i_end) 456 | { 457 | i64 value; 458 | M3Result result = ReadLebSigned (& value, 7, io_bytes, i_end); 459 | * o_value = (i8) value; 460 | 461 | return result; 462 | } 463 | 464 | 465 | M3Result ReadLEB_i32 (i32 * o_value, bytes_t * io_bytes, cbytes_t i_end) 466 | { 467 | i64 value; 468 | M3Result result = ReadLebSigned (& value, 32, io_bytes, i_end); 469 | * o_value = (i32) value; 470 | 471 | return result; 472 | } 473 | 474 | 475 | M3Result ReadLEB_i64 (i64 * o_value, bytes_t * io_bytes, cbytes_t i_end) 476 | { 477 | i64 value; 478 | M3Result result = ReadLebSigned (& value, 64, io_bytes, i_end); 479 | * o_value = value; 480 | 481 | return result; 482 | } 483 | 484 | 485 | M3Result Read_utf8 (cstr_t * o_utf8, bytes_t * io_bytes, cbytes_t i_end) 486 | { 487 | *o_utf8 = NULL; 488 | 489 | u32 utf8Length; 490 | M3Result result = ReadLEB_u32 (& utf8Length, io_bytes, i_end); 491 | 492 | if (not result) 493 | { 494 | if (utf8Length <= d_m3MaxSaneUtf8Length) 495 | { 496 | const u8 * ptr = * io_bytes; 497 | const u8 * end = ptr + utf8Length; 498 | 499 | if (end <= i_end) 500 | { 501 | char * utf8 = (char *)m3_Malloc ("UTF8", utf8Length + 1); 502 | 503 | if (utf8) 504 | { 505 | memcpy (utf8, ptr, utf8Length); 506 | utf8 [utf8Length] = 0; 507 | * o_utf8 = utf8; 508 | } 509 | 510 | * io_bytes = end; 511 | } 512 | else result = m3Err_wasmUnderrun; 513 | } 514 | else result = m3Err_missingUTF8; 515 | } 516 | 517 | return result; 518 | } 519 | 520 | #if d_m3RecordBacktraces 521 | u32 FindModuleOffset (IM3Runtime i_runtime, pc_t i_pc) 522 | { 523 | // walk the code pages 524 | IM3CodePage curr = i_runtime->pagesOpen; 525 | bool pageFound = false; 526 | 527 | while (curr) 528 | { 529 | if (ContainsPC (curr, i_pc)) 530 | { 531 | pageFound = true; 532 | break; 533 | } 534 | curr = curr->info.next; 535 | } 536 | 537 | if (!pageFound) 538 | { 539 | curr = i_runtime->pagesFull; 540 | while (curr) 541 | { 542 | if (ContainsPC (curr, i_pc)) 543 | { 544 | pageFound = true; 545 | break; 546 | } 547 | curr = curr->info.next; 548 | } 549 | } 550 | 551 | if (pageFound) 552 | { 553 | u32 result = 0; 554 | 555 | bool pcFound = MapPCToOffset (curr, i_pc, & result); 556 | d_m3Assert (pcFound); 557 | 558 | return result; 559 | } 560 | else return 0; 561 | } 562 | 563 | 564 | void PushBacktraceFrame (IM3Runtime io_runtime, pc_t i_pc) 565 | { 566 | // don't try to push any more frames if we've already had an alloc failure 567 | if (M3_UNLIKELY (io_runtime->backtrace.lastFrame == M3_BACKTRACE_TRUNCATED)) 568 | return; 569 | 570 | M3BacktraceFrame * newFrame = m3_AllocStruct(M3BacktraceFrame); 571 | 572 | if (!newFrame) 573 | { 574 | io_runtime->backtrace.lastFrame = M3_BACKTRACE_TRUNCATED; 575 | return; 576 | } 577 | 578 | newFrame->moduleOffset = FindModuleOffset (io_runtime, i_pc); 579 | 580 | if (!io_runtime->backtrace.frames || !io_runtime->backtrace.lastFrame) 581 | io_runtime->backtrace.frames = newFrame; 582 | else 583 | io_runtime->backtrace.lastFrame->next = newFrame; 584 | io_runtime->backtrace.lastFrame = newFrame; 585 | } 586 | 587 | 588 | void FillBacktraceFunctionInfo (IM3Runtime io_runtime, IM3Function i_function) 589 | { 590 | // If we've had an alloc failure then the last frame doesn't refer to the 591 | // frame we want to fill in the function info for. 592 | if (M3_UNLIKELY (io_runtime->backtrace.lastFrame == M3_BACKTRACE_TRUNCATED)) 593 | return; 594 | 595 | if (!io_runtime->backtrace.lastFrame) 596 | return; 597 | 598 | io_runtime->backtrace.lastFrame->function = i_function; 599 | } 600 | 601 | 602 | void ClearBacktrace (IM3Runtime io_runtime) 603 | { 604 | M3BacktraceFrame * currentFrame = io_runtime->backtrace.frames; 605 | while (currentFrame) 606 | { 607 | M3BacktraceFrame * nextFrame = currentFrame->next; 608 | m3_Free (currentFrame); 609 | currentFrame = nextFrame; 610 | } 611 | 612 | io_runtime->backtrace.frames = NULL; 613 | io_runtime->backtrace.lastFrame = NULL; 614 | } 615 | #endif // d_m3RecordBacktraces 616 | -------------------------------------------------------------------------------- /wasm3/m3_core.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_core.h 3 | // 4 | // Created by Steven Massey on 4/15/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | 8 | #ifndef m3_core_h 9 | #define m3_core_h 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "wasm3.h" 18 | #include "m3_config.h" 19 | 20 | # if defined(__cplusplus) 21 | # define d_m3BeginExternC extern "C" { 22 | # define d_m3EndExternC } 23 | # else 24 | # define d_m3BeginExternC 25 | # define d_m3EndExternC 26 | # endif 27 | 28 | d_m3BeginExternC 29 | 30 | #define d_m3ImplementFloat (d_m3HasFloat || d_m3NoFloatDynamic) 31 | 32 | #if !defined(d_m3ShortTypesDefined) 33 | 34 | typedef uint64_t u64; 35 | typedef int64_t i64; 36 | typedef uint32_t u32; 37 | typedef int32_t i32; 38 | typedef uint16_t u16; 39 | typedef int16_t i16; 40 | typedef uint8_t u8; 41 | typedef int8_t i8; 42 | 43 | #if d_m3ImplementFloat 44 | typedef double f64; 45 | typedef float f32; 46 | #endif 47 | 48 | #endif // d_m3ShortTypesDefined 49 | 50 | #define PRIf32 "f" 51 | #define PRIf64 "lf" 52 | 53 | typedef const void * m3ret_t; 54 | typedef const void * voidptr_t; 55 | typedef const char * cstr_t; 56 | typedef const char * const ccstr_t; 57 | typedef const u8 * bytes_t; 58 | typedef const u8 * const cbytes_t; 59 | 60 | typedef u16 m3opcode_t; 61 | 62 | typedef i64 m3reg_t; 63 | 64 | # if d_m3Use32BitSlots 65 | typedef u32 m3slot_t; 66 | # else 67 | typedef u64 m3slot_t; 68 | # endif 69 | 70 | typedef m3slot_t * m3stack_t; 71 | 72 | typedef 73 | const void * const cvptr_t; 74 | 75 | # if defined (DEBUG) 76 | 77 | # define d_m3Log(CATEGORY, FMT, ...) printf (" %8s | " FMT, #CATEGORY, ##__VA_ARGS__); 78 | 79 | # if d_m3LogParse 80 | # define m3log_parse(CATEGORY, FMT, ...) d_m3Log(CATEGORY, FMT, ##__VA_ARGS__) 81 | # else 82 | # define m3log_parse(...) {} 83 | # endif 84 | 85 | # if d_m3LogCompile 86 | # define m3log_compile(CATEGORY, FMT, ...) d_m3Log(CATEGORY, FMT, ##__VA_ARGS__) 87 | # else 88 | # define m3log_compile(...) {} 89 | # endif 90 | 91 | # if d_m3LogEmit 92 | # define m3log_emit(CATEGORY, FMT, ...) d_m3Log(CATEGORY, FMT, ##__VA_ARGS__) 93 | # else 94 | # define m3log_emit(...) {} 95 | # endif 96 | 97 | # if d_m3LogCodePages 98 | # define m3log_code(CATEGORY, FMT, ...) d_m3Log(CATEGORY, FMT, ##__VA_ARGS__) 99 | # else 100 | # define m3log_code(...) {} 101 | # endif 102 | 103 | # if d_m3LogModule 104 | # define m3log_module(CATEGORY, FMT, ...) d_m3Log(CATEGORY, FMT, ##__VA_ARGS__) 105 | # else 106 | # define m3log_module(...) {} 107 | # endif 108 | 109 | # if d_m3LogRuntime 110 | # define m3log_runtime(CATEGORY, FMT, ...) d_m3Log(CATEGORY, FMT, ##__VA_ARGS__) 111 | # else 112 | # define m3log_runtime(...) {} 113 | # endif 114 | 115 | # define m3log(CATEGORY, FMT, ...) m3log_##CATEGORY (CATEGORY, FMT "\n", ##__VA_ARGS__) 116 | # else 117 | # define d_m3Log(CATEGORY, FMT, ...) {} 118 | # define m3log(CATEGORY, FMT, ...) {} 119 | # endif 120 | 121 | 122 | # if defined(ASSERTS) || (defined(DEBUG) && !defined(NASSERTS)) 123 | # define d_m3Assert(ASS) if (!(ASS)) { printf("Assertion failed at %s:%d : %s\n", __FILE__, __LINE__, #ASS); abort(); } 124 | # else 125 | # define d_m3Assert(ASS) 126 | # endif 127 | 128 | typedef void /*const*/ * code_t; 129 | typedef code_t const * /*__restrict__*/ pc_t; 130 | 131 | 132 | typedef struct M3MemoryHeader 133 | { 134 | IM3Runtime runtime; 135 | void * maxStack; 136 | size_t length; 137 | } 138 | M3MemoryHeader; 139 | 140 | struct M3CodeMappingPage; 141 | 142 | typedef struct M3CodePageHeader 143 | { 144 | struct M3CodePage * next; 145 | 146 | u32 lineIndex; 147 | u32 numLines; 148 | u32 sequence; // this is just used for debugging; could be removed 149 | u32 usageCount; 150 | 151 | # if d_m3RecordBacktraces 152 | struct M3CodeMappingPage * mapping; 153 | # endif // d_m3RecordBacktraces 154 | } 155 | M3CodePageHeader; 156 | 157 | 158 | #define d_m3CodePageFreeLinesThreshold 4+2 // max is: select _sss & CallIndirect + 2 for bridge 159 | 160 | #define d_m3MemPageSize 65536 161 | 162 | #define d_m3Reg0SlotAlias 60000 163 | #define d_m3Fp0SlotAlias (d_m3Reg0SlotAlias + 2) 164 | 165 | #define d_m3MaxSaneTypesCount 100000 166 | #define d_m3MaxSaneFunctionsCount 100000 167 | #define d_m3MaxSaneImportsCount 10000 168 | #define d_m3MaxSaneExportsCount 10000 169 | #define d_m3MaxSaneGlobalsCount 100000 170 | #define d_m3MaxSaneElementSegments 100000 171 | #define d_m3MaxSaneDataSegments 100000 172 | #define d_m3MaxSaneTableSize 100000 173 | #define d_m3MaxSaneUtf8Length 10000 174 | #define d_m3MaxSaneFunctionArgRetCount 1000 // still insane, but whatever 175 | 176 | #define d_externalKind_function 0 177 | #define d_externalKind_table 1 178 | #define d_externalKind_memory 2 179 | #define d_externalKind_global 3 180 | 181 | static const char * const c_waTypes [] = { "nil", "i32", "i64", "f32", "f64", "unknown" }; 182 | static const char * const c_waCompactTypes [] = { "_", "i", "I", "f", "F", "?" }; 183 | 184 | 185 | # if d_m3VerboseErrorMessages 186 | 187 | M3Result m3Error (M3Result i_result, IM3Runtime i_runtime, IM3Module i_module, IM3Function i_function, 188 | const char * const i_file, u32 i_lineNum, const char * const i_errorMessage, ...); 189 | 190 | # define _m3Error(RESULT, RT, MOD, FUN, FILE, LINE, FORMAT, ...) \ 191 | m3Error (RESULT, RT, MOD, FUN, FILE, LINE, FORMAT, ##__VA_ARGS__) 192 | 193 | # else 194 | # define _m3Error(RESULT, RT, MOD, FUN, FILE, LINE, FORMAT, ...) (RESULT) 195 | # endif 196 | 197 | #define ErrorRuntime(RESULT, RUNTIME, FORMAT, ...) _m3Error (RESULT, RUNTIME, NULL, NULL, __FILE__, __LINE__, FORMAT, ##__VA_ARGS__) 198 | #define ErrorModule(RESULT, MOD, FORMAT, ...) _m3Error (RESULT, MOD->runtime, MOD, NULL, __FILE__, __LINE__, FORMAT, ##__VA_ARGS__) 199 | #define ErrorCompile(RESULT, COMP, FORMAT, ...) _m3Error (RESULT, COMP->runtime, COMP->module, NULL, __FILE__, __LINE__, FORMAT, ##__VA_ARGS__) 200 | 201 | #if d_m3LogNativeStack 202 | void m3StackCheckInit (); 203 | void m3StackCheck (); 204 | int m3StackGetMax (); 205 | #else 206 | #define m3StackCheckInit() 207 | #define m3StackCheck() 208 | #define m3StackGetMax() 0 209 | #endif 210 | 211 | #if d_m3LogTimestamps 212 | #define PRIts "%llu" 213 | uint64_t m3_GetTimestamp (); 214 | #else 215 | #define PRIts "%s" 216 | #define m3_GetTimestamp() "" 217 | #endif 218 | 219 | void m3_Abort (const char* message); 220 | void * m3_Malloc_Impl (size_t i_size); 221 | void * m3_Realloc_Impl (void * i_ptr, size_t i_newSize, size_t i_oldSize); 222 | void m3_Free_Impl (void * i_ptr); 223 | void * m3_CopyMem (const void * i_from, size_t i_size); 224 | 225 | #if d_m3LogHeapOps 226 | 227 | // Tracing format: timestamp;heap:OpCode;name;size(bytes);new items;new ptr;old items;old ptr 228 | 229 | static inline void * m3_AllocStruct_Impl(ccstr_t name, size_t i_size) { 230 | void * result = m3_Malloc_Impl(i_size); 231 | fprintf(stderr, PRIts ";heap:AllocStruct;%s;%zu;;%p;;\n", m3_GetTimestamp(), name, i_size, result); 232 | return result; 233 | } 234 | 235 | static inline void * m3_AllocArray_Impl(ccstr_t name, size_t i_num, size_t i_size) { 236 | void * result = m3_Malloc_Impl(i_size * i_num); 237 | fprintf(stderr, PRIts ";heap:AllocArr;%s;%zu;%zu;%p;;\n", m3_GetTimestamp(), name, i_size, i_num, result); 238 | return result; 239 | } 240 | 241 | static inline void * m3_ReallocArray_Impl(ccstr_t name, void * i_ptr_old, size_t i_num_new, size_t i_num_old, size_t i_size) { 242 | void * result = m3_Realloc_Impl (i_ptr_old, i_size * i_num_new, i_size * i_num_old); 243 | fprintf(stderr, PRIts ";heap:ReallocArr;%s;%zu;%zu;%p;%zu;%p\n", m3_GetTimestamp(), name, i_size, i_num_new, result, i_num_old, i_ptr_old); 244 | return result; 245 | } 246 | 247 | static inline void * m3_Malloc (ccstr_t name, size_t i_size) { 248 | void * result = m3_Malloc_Impl (i_size); 249 | fprintf(stderr, PRIts ";heap:AllocMem;%s;%zu;;%p;;\n", m3_GetTimestamp(), name, i_size, result); 250 | return result; 251 | } 252 | static inline void * m3_Realloc (ccstr_t name, void * i_ptr, size_t i_newSize, size_t i_oldSize) { 253 | void * result = m3_Realloc_Impl (i_ptr, i_newSize, i_oldSize); 254 | fprintf(stderr, PRIts ";heap:ReallocMem;%s;;%zu;%p;%zu;%p\n", m3_GetTimestamp(), name, i_newSize, result, i_oldSize, i_ptr); 255 | return result; 256 | } 257 | 258 | #define m3_AllocStruct(STRUCT) (STRUCT *)m3_AllocStruct_Impl (#STRUCT, sizeof (STRUCT)) 259 | #define m3_AllocArray(STRUCT, NUM) (STRUCT *)m3_AllocArray_Impl (#STRUCT, NUM, sizeof (STRUCT)) 260 | #define m3_ReallocArray(STRUCT, PTR, NEW, OLD) (STRUCT *)m3_ReallocArray_Impl (#STRUCT, (void *)(PTR), (NEW), (OLD), sizeof (STRUCT)) 261 | #define m3_Free(P) do { void* p = (void*)(P); \ 262 | if (p) { fprintf(stderr, PRIts ";heap:FreeMem;;;;%p;\n", m3_GetTimestamp(), p); } \ 263 | m3_Free_Impl (p); (P) = NULL; } while(0) 264 | #else 265 | #define m3_Malloc(NAME, SIZE) m3_Malloc_Impl(SIZE) 266 | #define m3_Realloc(NAME, PTR, NEW, OLD) m3_Realloc_Impl(PTR, NEW, OLD) 267 | #define m3_AllocStruct(STRUCT) (STRUCT *)m3_Malloc_Impl (sizeof (STRUCT)) 268 | #define m3_AllocArray(STRUCT, NUM) (STRUCT *)m3_Malloc_Impl (sizeof (STRUCT) * (NUM)) 269 | #define m3_ReallocArray(STRUCT, PTR, NEW, OLD) (STRUCT *)m3_Realloc_Impl ((void *)(PTR), sizeof (STRUCT) * (NEW), sizeof (STRUCT) * (OLD)) 270 | #define m3_Free(P) do { m3_Free_Impl ((void*)(P)); (P) = NULL; } while(0) 271 | #endif 272 | 273 | M3Result NormalizeType (u8 * o_type, i8 i_convolutedWasmType); 274 | 275 | bool IsIntType (u8 i_wasmType); 276 | bool IsFpType (u8 i_wasmType); 277 | bool Is64BitType (u8 i_m3Type); 278 | u32 SizeOfType (u8 i_m3Type); 279 | 280 | M3Result Read_u64 (u64 * o_value, bytes_t * io_bytes, cbytes_t i_end); 281 | M3Result Read_u32 (u32 * o_value, bytes_t * io_bytes, cbytes_t i_end); 282 | #if d_m3ImplementFloat 283 | M3Result Read_f64 (f64 * o_value, bytes_t * io_bytes, cbytes_t i_end); 284 | M3Result Read_f32 (f32 * o_value, bytes_t * io_bytes, cbytes_t i_end); 285 | #endif 286 | M3Result Read_u8 (u8 * o_value, bytes_t * io_bytes, cbytes_t i_end); 287 | M3Result Read_opcode (m3opcode_t * o_value, bytes_t * io_bytes, cbytes_t i_end); 288 | 289 | M3Result ReadLebUnsigned (u64 * o_value, u32 i_maxNumBits, bytes_t * io_bytes, cbytes_t i_end); 290 | M3Result ReadLebSigned (i64 * o_value, u32 i_maxNumBits, bytes_t * io_bytes, cbytes_t i_end); 291 | M3Result ReadLEB_u32 (u32 * o_value, bytes_t * io_bytes, cbytes_t i_end); 292 | M3Result ReadLEB_u7 (u8 * o_value, bytes_t * io_bytes, cbytes_t i_end); 293 | M3Result ReadLEB_i7 (i8 * o_value, bytes_t * io_bytes, cbytes_t i_end); 294 | M3Result ReadLEB_i32 (i32 * o_value, bytes_t * io_bytes, cbytes_t i_end); 295 | M3Result ReadLEB_i64 (i64 * o_value, bytes_t * io_bytes, cbytes_t i_end); 296 | M3Result Read_utf8 (cstr_t * o_utf8, bytes_t * io_bytes, cbytes_t i_end); 297 | 298 | cstr_t SPrintValue (void * i_value, u8 i_type); 299 | size_t SPrintArg (char * o_string, size_t i_stringBufferSize, voidptr_t i_sp, u8 i_type); 300 | 301 | void ReportError (IM3Runtime io_runtime, IM3Module i_module, IM3Function i_function, ccstr_t i_errorMessage, ccstr_t i_file, u32 i_lineNum); 302 | 303 | # if d_m3RecordBacktraces 304 | void PushBacktraceFrame (IM3Runtime io_runtime, pc_t i_pc); 305 | void FillBacktraceFunctionInfo (IM3Runtime io_runtime, IM3Function i_function); 306 | void ClearBacktrace (IM3Runtime io_runtime); 307 | # endif 308 | 309 | d_m3EndExternC 310 | 311 | #endif // m3_core_h 312 | -------------------------------------------------------------------------------- /wasm3/m3_env.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_env.h 3 | // 4 | // Created by Steven Massey on 4/19/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | 8 | #ifndef m3_env_h 9 | #define m3_env_h 10 | 11 | #include "wasm3.h" 12 | #include "m3_code.h" 13 | #include "m3_compile.h" 14 | 15 | d_m3BeginExternC 16 | 17 | 18 | //--------------------------------------------------------------------------------------------------------------------------------- 19 | 20 | typedef struct M3MemoryInfo 21 | { 22 | u32 initPages; 23 | u32 maxPages; 24 | } 25 | M3MemoryInfo; 26 | 27 | 28 | typedef struct M3Memory 29 | { 30 | M3MemoryHeader * mallocated; 31 | 32 | u32 numPages; 33 | u32 maxPages; 34 | } 35 | M3Memory; 36 | 37 | typedef M3Memory * IM3Memory; 38 | 39 | 40 | //--------------------------------------------------------------------------------------------------------------------------------- 41 | 42 | typedef struct M3DataSegment 43 | { 44 | const u8 * initExpr; // wasm code 45 | const u8 * data; 46 | 47 | u32 initExprSize; 48 | u32 memoryRegion; 49 | u32 size; 50 | } 51 | M3DataSegment; 52 | 53 | //--------------------------------------------------------------------------------------------------------------------------------- 54 | 55 | typedef struct M3Global 56 | { 57 | M3ImportInfo import; 58 | 59 | union 60 | { 61 | i64 intValue; 62 | #if d_m3HasFloat 63 | f64 f64Value; 64 | f32 f32Value; 65 | #endif 66 | }; 67 | 68 | cstr_t name; 69 | bytes_t initExpr; // wasm code 70 | u32 initExprSize; 71 | u8 type; 72 | bool imported; 73 | bool isMutable; 74 | } 75 | M3Global; 76 | 77 | 78 | //--------------------------------------------------------------------------------------------------------------------------------- 79 | typedef struct M3Module 80 | { 81 | struct M3Runtime * runtime; 82 | struct M3Environment * environment; 83 | 84 | bytes_t wasmStart; 85 | bytes_t wasmEnd; 86 | 87 | cstr_t name; 88 | 89 | u32 numFuncTypes; 90 | IM3FuncType * funcTypes; // array of pointers to list of FuncTypes 91 | 92 | u32 numFuncImports; 93 | u32 numFunctions; 94 | u32 allFunctions; // allocated functions count 95 | M3Function * functions; 96 | 97 | i32 startFunction; 98 | 99 | u32 numDataSegments; 100 | M3DataSegment * dataSegments; 101 | 102 | //u32 importedGlobals; 103 | u32 numGlobals; 104 | M3Global * globals; 105 | 106 | u32 numElementSegments; 107 | bytes_t elementSection; 108 | bytes_t elementSectionEnd; 109 | 110 | IM3Function * table0; 111 | u32 table0Size; 112 | 113 | M3MemoryInfo memoryInfo; 114 | bool memoryImported; 115 | 116 | //bool hasWasmCodeCopy; 117 | 118 | struct M3Module * next; 119 | } 120 | M3Module; 121 | 122 | M3Result Module_AddGlobal (IM3Module io_module, IM3Global * o_global, u8 i_type, bool i_mutable, bool i_isImported); 123 | 124 | M3Result Module_PreallocFunctions (IM3Module io_module, u32 i_totalFunctions); 125 | M3Result Module_AddFunction (IM3Module io_module, u32 i_typeIndex, IM3ImportInfo i_importInfo /* can be null */); 126 | IM3Function Module_GetFunction (IM3Module i_module, u32 i_functionIndex); 127 | 128 | void Module_GenerateNames (IM3Module i_module); 129 | 130 | void FreeImportInfo (M3ImportInfo * i_info); 131 | 132 | //--------------------------------------------------------------------------------------------------------------------------------- 133 | 134 | typedef struct M3Environment 135 | { 136 | // struct M3Runtime * runtimes; 137 | 138 | IM3FuncType funcTypes; // linked list of unique M3FuncType structs that can be compared using pointer-equivalence 139 | 140 | IM3FuncType retFuncTypes [c_m3Type_unknown]; // these 'point' to elements in the linked list above. 141 | // the number of elements must match the basic types as per M3ValueType 142 | M3CodePage * pagesReleased; 143 | 144 | M3SectionHandler customSectionHandler; 145 | } 146 | M3Environment; 147 | 148 | void Environment_Release (IM3Environment i_environment); 149 | 150 | // takes ownership of io_funcType and returns a pointer to the persistent version (could be same or different) 151 | void Environment_AddFuncType (IM3Environment i_environment, IM3FuncType * io_funcType); 152 | 153 | //--------------------------------------------------------------------------------------------------------------------------------- 154 | 155 | typedef struct M3Runtime 156 | { 157 | M3Compilation compilation; 158 | 159 | IM3Environment environment; 160 | 161 | M3CodePage * pagesOpen; // linked list of code pages with writable space on them 162 | M3CodePage * pagesFull; // linked list of at-capacity pages 163 | 164 | u32 numCodePages; 165 | u32 numActiveCodePages; 166 | 167 | IM3Module modules; // linked list of imported modules 168 | 169 | void * stack; 170 | u32 stackSize; 171 | u32 numStackSlots; 172 | IM3Function lastCalled; // last function that successfully executed 173 | 174 | void * userdata; 175 | 176 | M3Memory memory; 177 | u32 memoryLimit; 178 | 179 | #if d_m3EnableStrace >= 2 180 | u32 callDepth; 181 | #endif 182 | 183 | M3ErrorInfo error; 184 | #if d_m3VerboseErrorMessages 185 | char error_message[256]; // the actual buffer. M3ErrorInfo can point to this 186 | #endif 187 | 188 | #if d_m3RecordBacktraces 189 | M3BacktraceInfo backtrace; 190 | #endif 191 | 192 | u32 newCodePageSequence; 193 | } 194 | M3Runtime; 195 | 196 | void InitRuntime (IM3Runtime io_runtime, u32 i_stackSizeInBytes); 197 | void Runtime_Release (IM3Runtime io_runtime); 198 | 199 | M3Result ResizeMemory (IM3Runtime io_runtime, u32 i_numPages); 200 | 201 | typedef void * (* ModuleVisitor) (IM3Module i_module, void * i_info); 202 | void * ForEachModule (IM3Runtime i_runtime, ModuleVisitor i_visitor, void * i_info); 203 | 204 | void * v_FindFunction (IM3Module i_module, const char * const i_name); 205 | 206 | IM3CodePage AcquireCodePage (IM3Runtime io_runtime); 207 | IM3CodePage AcquireCodePageWithCapacity (IM3Runtime io_runtime, u32 i_lineCount); 208 | void ReleaseCodePage (IM3Runtime io_runtime, IM3CodePage i_codePage); 209 | 210 | d_m3EndExternC 211 | 212 | #endif // m3_env_h 213 | -------------------------------------------------------------------------------- /wasm3/m3_exception.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_exception.h 3 | // 4 | // Created by Steven Massey on 7/5/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | // some macros to emulate try/catch 8 | 9 | #ifndef m3_exception_h 10 | #define m3_exception_h 11 | 12 | #include "m3_config.h" 13 | 14 | # if d_m3EnableExceptionBreakpoint 15 | 16 | // declared in m3_info.c 17 | void ExceptionBreakpoint (cstr_t i_exception, cstr_t i_message); 18 | 19 | # define EXCEPTION_PRINT(ERROR) ExceptionBreakpoint (ERROR, (__FILE__ ":" M3_STR(__LINE__))) 20 | 21 | # else 22 | # define EXCEPTION_PRINT(...) 23 | # endif 24 | 25 | 26 | #define _try M3Result result = m3Err_none; 27 | #define _(TRY) { result = TRY; if (M3_UNLIKELY(result)) { EXCEPTION_PRINT (result); goto _catch; } } 28 | #define _throw(ERROR) { result = ERROR; EXCEPTION_PRINT (result); goto _catch; } 29 | #define _throwif(ERROR, COND) if (M3_UNLIKELY(COND)) { _throw(ERROR); } 30 | 31 | #define _throwifnull(PTR) _throwif (m3Err_mallocFailed, !(PTR)) 32 | 33 | #endif // m3_exception_h 34 | -------------------------------------------------------------------------------- /wasm3/m3_exec.c: -------------------------------------------------------------------------------- 1 | // 2 | // m3_exec.c 3 | // 4 | // Created by Steven Massey on 4/17/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | 8 | // EMPTY FOR NOW 9 | -------------------------------------------------------------------------------- /wasm3/m3_exec_defs.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_exec_defs.h 3 | // 4 | // Created by Steven Massey on 5/1/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | 8 | #ifndef m3_exec_defs_h 9 | #define m3_exec_defs_h 10 | 11 | #include "m3_core.h" 12 | 13 | d_m3BeginExternC 14 | 15 | # define m3MemData(mem) (u8*)(((M3MemoryHeader*)(mem))+1) 16 | # define m3MemRuntime(mem) (((M3MemoryHeader*)(mem))->runtime) 17 | # define m3MemInfo(mem) (&(((M3MemoryHeader*)(mem))->runtime->memory)) 18 | 19 | # define d_m3BaseOpSig pc_t _pc, m3stack_t _sp, M3MemoryHeader * _mem, m3reg_t _r0 20 | # define d_m3BaseOpArgs _sp, _mem, _r0 21 | # define d_m3BaseOpAllArgs _pc, _sp, _mem, _r0 22 | # define d_m3BaseOpDefaultArgs 0 23 | # define d_m3BaseClearRegisters _r0 = 0; 24 | 25 | # define d_m3ExpOpSig(...) d_m3BaseOpSig, __VA_ARGS__ 26 | # define d_m3ExpOpArgs(...) d_m3BaseOpArgs, __VA_ARGS__ 27 | # define d_m3ExpOpAllArgs(...) d_m3BaseOpAllArgs, __VA_ARGS__ 28 | # define d_m3ExpOpDefaultArgs(...) d_m3BaseOpDefaultArgs, __VA_ARGS__ 29 | # define d_m3ExpClearRegisters(...) d_m3BaseClearRegisters; __VA_ARGS__ 30 | 31 | # if d_m3HasFloat 32 | # define d_m3OpSig d_m3ExpOpSig (f64 _fp0) 33 | # define d_m3OpArgs d_m3ExpOpArgs (_fp0) 34 | # define d_m3OpAllArgs d_m3ExpOpAllArgs (_fp0) 35 | # define d_m3OpDefaultArgs d_m3ExpOpDefaultArgs (0.) 36 | # define d_m3ClearRegisters d_m3ExpClearRegisters (_fp0 = 0.;) 37 | # else 38 | # define d_m3OpSig d_m3BaseOpSig 39 | # define d_m3OpArgs d_m3BaseOpArgs 40 | # define d_m3OpAllArgs d_m3BaseOpAllArgs 41 | # define d_m3OpDefaultArgs d_m3BaseOpDefaultArgs 42 | # define d_m3ClearRegisters d_m3BaseClearRegisters 43 | # endif 44 | 45 | typedef m3ret_t (vectorcall * IM3Operation) (d_m3OpSig); 46 | 47 | #define d_m3RetSig static inline m3ret_t vectorcall 48 | #define d_m3Op(NAME) M3_NO_UBSAN d_m3RetSig op_##NAME (d_m3OpSig) 49 | 50 | #define nextOpImpl() ((IM3Operation)(* _pc))(_pc + 1, d_m3OpArgs) 51 | #define jumpOpImpl(PC) ((IM3Operation)(* PC))( PC + 1, d_m3OpArgs) 52 | 53 | #define nextOpDirect() return nextOpImpl() 54 | #define jumpOpDirect(PC) return jumpOpImpl((pc_t)(PC)) 55 | 56 | d_m3RetSig RunCode (d_m3OpSig) 57 | { 58 | nextOpDirect(); 59 | } 60 | 61 | d_m3EndExternC 62 | 63 | #endif // m3_exec_defs_h 64 | -------------------------------------------------------------------------------- /wasm3/m3_function.c: -------------------------------------------------------------------------------- 1 | // 2 | // m3_function.c 3 | // 4 | // Created by Steven Massey on 4/7/21. 5 | // Copyright © 2021 Steven Massey. All rights reserved. 6 | // 7 | 8 | #include "m3_function.h" 9 | #include "m3_env.h" 10 | 11 | 12 | M3Result AllocFuncType (IM3FuncType * o_functionType, u32 i_numTypes) 13 | { 14 | *o_functionType = (IM3FuncType) m3_Malloc ("M3FuncType", sizeof (M3FuncType) + i_numTypes); 15 | return (*o_functionType) ? m3Err_none : m3Err_mallocFailed; 16 | } 17 | 18 | 19 | bool AreFuncTypesEqual (const IM3FuncType i_typeA, const IM3FuncType i_typeB) 20 | { 21 | if (i_typeA->numRets == i_typeB->numRets && i_typeA->numArgs == i_typeB->numArgs) 22 | { 23 | return (memcmp (i_typeA->types, i_typeB->types, i_typeA->numRets + i_typeA->numArgs) == 0); 24 | } 25 | 26 | return false; 27 | } 28 | 29 | u16 GetFuncTypeNumParams (const IM3FuncType i_funcType) 30 | { 31 | return i_funcType ? i_funcType->numArgs : 0; 32 | } 33 | 34 | 35 | u8 GetFuncTypeParamType (const IM3FuncType i_funcType, u16 i_index) 36 | { 37 | u8 type = c_m3Type_unknown; 38 | 39 | if (i_funcType) 40 | { 41 | if (i_index < i_funcType->numArgs) 42 | { 43 | type = i_funcType->types [i_funcType->numRets + i_index]; 44 | } 45 | } 46 | 47 | return type; 48 | } 49 | 50 | 51 | 52 | u16 GetFuncTypeNumResults (const IM3FuncType i_funcType) 53 | { 54 | return i_funcType ? i_funcType->numRets : 0; 55 | } 56 | 57 | 58 | u8 GetFuncTypeResultType (const IM3FuncType i_funcType, u16 i_index) 59 | { 60 | u8 type = c_m3Type_unknown; 61 | 62 | if (i_funcType) 63 | { 64 | if (i_index < i_funcType->numRets) 65 | { 66 | type = i_funcType->types [i_index]; 67 | } 68 | } 69 | 70 | return type; 71 | } 72 | 73 | 74 | //--------------------------------------------------------------------------------------------------------------- 75 | 76 | 77 | void FreeImportInfo (M3ImportInfo * i_info) 78 | { 79 | m3_Free (i_info->moduleUtf8); 80 | m3_Free (i_info->fieldUtf8); 81 | } 82 | 83 | 84 | void Function_Release (IM3Function i_function) 85 | { 86 | m3_Free (i_function->constants); 87 | 88 | for (int i = 0; i < i_function->numNames; i++) 89 | { 90 | // name can be an alias of fieldUtf8 91 | if (i_function->names[i] != i_function->import.fieldUtf8) 92 | { 93 | m3_Free (i_function->names[i]); 94 | } 95 | } 96 | 97 | FreeImportInfo (& i_function->import); 98 | 99 | if (i_function->ownsWasmCode) 100 | m3_Free (i_function->wasm); 101 | 102 | // Function_FreeCompiledCode (func); 103 | 104 | # if (d_m3EnableCodePageRefCounting) 105 | { 106 | m3_Free (i_function->codePageRefs); 107 | i_function->numCodePageRefs = 0; 108 | } 109 | # endif 110 | } 111 | 112 | 113 | void Function_FreeCompiledCode (IM3Function i_function) 114 | { 115 | # if (d_m3EnableCodePageRefCounting) 116 | { 117 | i_function->compiled = NULL; 118 | 119 | while (i_function->numCodePageRefs--) 120 | { 121 | IM3CodePage page = i_function->codePageRefs [i_function->numCodePageRefs]; 122 | 123 | if (--(page->info.usageCount) == 0) 124 | { 125 | // printf ("free %p\n", page); 126 | } 127 | } 128 | 129 | m3_Free (i_function->codePageRefs); 130 | 131 | Runtime_ReleaseCodePages (i_function->module->runtime); 132 | } 133 | # endif 134 | } 135 | 136 | 137 | cstr_t m3_GetFunctionName (IM3Function i_function) 138 | { 139 | u16 numNames = 0; 140 | cstr_t *names = GetFunctionNames(i_function, &numNames); 141 | if (numNames > 0) 142 | return names[0]; 143 | else 144 | return ""; 145 | } 146 | 147 | 148 | IM3Module m3_GetFunctionModule (IM3Function i_function) 149 | { 150 | return i_function ? i_function->module : NULL; 151 | } 152 | 153 | 154 | cstr_t * GetFunctionNames (IM3Function i_function, u16 * o_numNames) 155 | { 156 | if (!i_function || !o_numNames) 157 | return NULL; 158 | 159 | if (i_function->import.fieldUtf8) 160 | { 161 | *o_numNames = 1; 162 | return &i_function->import.fieldUtf8; 163 | } 164 | else 165 | { 166 | *o_numNames = i_function->numNames; 167 | return i_function->names; 168 | } 169 | } 170 | 171 | 172 | cstr_t GetFunctionImportModuleName (IM3Function i_function) 173 | { 174 | return (i_function->import.moduleUtf8) ? i_function->import.moduleUtf8 : ""; 175 | } 176 | 177 | 178 | u16 GetFunctionNumArgs (IM3Function i_function) 179 | { 180 | u16 numArgs = 0; 181 | 182 | if (i_function) 183 | { 184 | if (i_function->funcType) 185 | numArgs = i_function->funcType->numArgs; 186 | } 187 | 188 | return numArgs; 189 | } 190 | 191 | u8 GetFunctionArgType (IM3Function i_function, u32 i_index) 192 | { 193 | u8 type = c_m3Type_none; 194 | 195 | if (i_index < GetFunctionNumArgs (i_function)) 196 | { 197 | u32 numReturns = i_function->funcType->numRets; 198 | 199 | type = i_function->funcType->types [numReturns + i_index]; 200 | } 201 | 202 | return type; 203 | } 204 | 205 | 206 | u16 GetFunctionNumReturns (IM3Function i_function) 207 | { 208 | u16 numReturns = 0; 209 | 210 | if (i_function) 211 | { 212 | if (i_function->funcType) 213 | numReturns = i_function->funcType->numRets; 214 | } 215 | 216 | return numReturns; 217 | } 218 | 219 | 220 | u8 GetFunctionReturnType (const IM3Function i_function, u16 i_index) 221 | { 222 | return i_function ? GetFuncTypeResultType (i_function->funcType, i_index) : c_m3Type_unknown; 223 | } 224 | 225 | 226 | u32 GetFunctionNumArgsAndLocals (IM3Function i_function) 227 | { 228 | if (i_function) 229 | return i_function->numLocals + GetFunctionNumArgs (i_function); 230 | else 231 | return 0; 232 | } 233 | 234 | -------------------------------------------------------------------------------- /wasm3/m3_function.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_function.h 3 | // 4 | // Created by Steven Massey on 4/7/21. 5 | // Copyright © 2021 Steven Massey. All rights reserved. 6 | // 7 | 8 | #ifndef m3_function_h 9 | #define m3_function_h 10 | 11 | #include "m3_core.h" 12 | 13 | d_m3BeginExternC 14 | 15 | //--------------------------------------------------------------------------------------------------------------------------------- 16 | 17 | typedef struct M3FuncType 18 | { 19 | struct M3FuncType * next; 20 | 21 | u16 numRets; 22 | u16 numArgs; 23 | u8 types []; // returns, then args 24 | } 25 | M3FuncType; 26 | 27 | typedef M3FuncType * IM3FuncType; 28 | 29 | 30 | M3Result AllocFuncType (IM3FuncType * o_functionType, u32 i_numTypes); 31 | bool AreFuncTypesEqual (const IM3FuncType i_typeA, const IM3FuncType i_typeB); 32 | 33 | u16 GetFuncTypeNumParams (const IM3FuncType i_funcType); 34 | u8 GetFuncTypeParamType (const IM3FuncType i_funcType, u16 i_index); 35 | 36 | u16 GetFuncTypeNumResults (const IM3FuncType i_funcType); 37 | u8 GetFuncTypeResultType (const IM3FuncType i_funcType, u16 i_index); 38 | 39 | //--------------------------------------------------------------------------------------------------------------------------------- 40 | 41 | typedef struct M3Function 42 | { 43 | struct M3Module * module; 44 | 45 | M3ImportInfo import; 46 | 47 | bytes_t wasm; 48 | bytes_t wasmEnd; 49 | 50 | cstr_t names[d_m3MaxDuplicateFunctionImpl]; 51 | u16 numNames; // maximum of d_m3MaxDuplicateFunctionImpl 52 | 53 | IM3FuncType funcType; 54 | 55 | pc_t compiled; 56 | 57 | # if (d_m3EnableCodePageRefCounting) 58 | IM3CodePage * codePageRefs; // array of all pages used 59 | u32 numCodePageRefs; 60 | # endif 61 | 62 | # if defined (DEBUG) 63 | u32 hits; 64 | u32 index; 65 | # endif 66 | 67 | u16 maxStackSlots; 68 | 69 | u16 numRetSlots; 70 | u16 numRetAndArgSlots; 71 | 72 | u16 numLocals; // not including args 73 | u16 numLocalBytes; 74 | 75 | bool ownsWasmCode; 76 | 77 | u16 numConstantBytes; 78 | void * constants; 79 | } 80 | M3Function; 81 | 82 | void Function_Release (IM3Function i_function); 83 | void Function_FreeCompiledCode (IM3Function i_function); 84 | 85 | cstr_t GetFunctionImportModuleName (IM3Function i_function); 86 | cstr_t * GetFunctionNames (IM3Function i_function, u16 * o_numNames); 87 | u16 GetFunctionNumArgs (IM3Function i_function); 88 | u8 GetFunctionArgType (IM3Function i_function, u32 i_index); 89 | 90 | u16 GetFunctionNumReturns (IM3Function i_function); 91 | u8 GetFunctionReturnType (const IM3Function i_function, u16 i_index); 92 | 93 | u32 GetFunctionNumArgsAndLocals (IM3Function i_function); 94 | 95 | cstr_t SPrintFunctionArgList (IM3Function i_function, m3stack_t i_sp); 96 | 97 | //--------------------------------------------------------------------------------------------------------------------------------- 98 | 99 | 100 | d_m3EndExternC 101 | 102 | #endif /* m3_function_h */ 103 | -------------------------------------------------------------------------------- /wasm3/m3_info.c: -------------------------------------------------------------------------------- 1 | // 2 | // m3_info.c 3 | // 4 | // Created by Steven Massey on 4/27/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | 8 | #include "m3_env.h" 9 | #include "m3_info.h" 10 | #include "m3_compile.h" 11 | 12 | #ifdef DEBUG 13 | 14 | // a central function you can be breakpoint: 15 | void ExceptionBreakpoint (cstr_t i_exception, cstr_t i_message) 16 | { 17 | printf ("\nexception: '%s' @ %s\n", i_exception, i_message); 18 | return; 19 | } 20 | 21 | 22 | typedef struct OpInfo 23 | { 24 | IM3OpInfo info; 25 | m3opcode_t opcode; 26 | } 27 | OpInfo; 28 | 29 | void m3_PrintM3Info () 30 | { 31 | printf ("\n-- m3 configuration --------------------------------------------\n"); 32 | // printf (" sizeof M3CodePage : %zu bytes (%d slots) \n", sizeof (M3CodePage), c_m3CodePageNumSlots); 33 | printf (" sizeof M3MemPage : %u bytes \n", d_m3MemPageSize); 34 | printf (" sizeof M3Compilation : %zu bytes \n", sizeof (M3Compilation)); 35 | printf (" sizeof M3Function : %zu bytes \n", sizeof (M3Function)); 36 | printf ("----------------------------------------------------------------\n\n"); 37 | } 38 | 39 | 40 | void * v_PrintEnvModuleInfo (IM3Module i_module, u32 * io_index) 41 | { 42 | printf (" module [%u] name: '%s'; funcs: %d \n", * io_index++, i_module->name, i_module->numFunctions); 43 | 44 | return NULL; 45 | } 46 | 47 | 48 | void m3_PrintRuntimeInfo (IM3Runtime i_runtime) 49 | { 50 | printf ("\n-- m3 runtime -------------------------------------------------\n"); 51 | 52 | printf (" stack-size: %zu \n\n", i_runtime->numStackSlots * sizeof (m3slot_t)); 53 | 54 | u32 moduleIndex = 0; 55 | ForEachModule (i_runtime, (ModuleVisitor) v_PrintEnvModuleInfo, & moduleIndex); 56 | 57 | printf ("----------------------------------------------------------------\n\n"); 58 | } 59 | 60 | 61 | cstr_t GetTypeName (u8 i_m3Type) 62 | { 63 | if (i_m3Type < 5) 64 | return c_waTypes [i_m3Type]; 65 | else 66 | return "?"; 67 | } 68 | 69 | 70 | // TODO: these 'static char string []' aren't thread-friendly. though these functions are 71 | // mainly for simple diagnostics during development, it'd be nice if they were fully reliable. 72 | 73 | cstr_t SPrintFuncTypeSignature (IM3FuncType i_funcType) 74 | { 75 | static char string [256]; 76 | 77 | sprintf (string, "("); 78 | 79 | for (u32 i = 0; i < i_funcType->numArgs; ++i) 80 | { 81 | if (i != 0) 82 | strcat (string, ", "); 83 | 84 | strcat (string, GetTypeName (d_FuncArgType(i_funcType, i))); 85 | } 86 | 87 | strcat (string, ") -> "); 88 | 89 | for (u32 i = 0; i < i_funcType->numRets; ++i) 90 | { 91 | if (i != 0) 92 | strcat (string, ", "); 93 | 94 | strcat (string, GetTypeName (d_FuncRetType(i_funcType, i))); 95 | } 96 | 97 | return string; 98 | } 99 | 100 | 101 | size_t SPrintArg (char * o_string, size_t i_stringBufferSize, voidptr_t i_sp, u8 i_type) 102 | { 103 | int len = 0; 104 | 105 | * o_string = 0; 106 | 107 | if (i_type == c_m3Type_i32) 108 | len = snprintf (o_string, i_stringBufferSize, "%" PRIi32, * (i32 *) i_sp); 109 | else if (i_type == c_m3Type_i64) 110 | len = snprintf (o_string, i_stringBufferSize, "%" PRIi64, * (i64 *) i_sp); 111 | #if d_m3HasFloat 112 | else if (i_type == c_m3Type_f32) 113 | len = snprintf (o_string, i_stringBufferSize, "%" PRIf32, * (f32 *) i_sp); 114 | else if (i_type == c_m3Type_f64) 115 | len = snprintf (o_string, i_stringBufferSize, "%" PRIf64, * (f64 *) i_sp); 116 | #endif 117 | 118 | len = M3_MAX (0, len); 119 | 120 | return len; 121 | } 122 | 123 | 124 | cstr_t SPrintValue (void * i_value, u8 i_type) 125 | { 126 | static char string [100]; 127 | SPrintArg (string, 100, (m3stack_t) i_value, i_type); 128 | return string; 129 | } 130 | 131 | 132 | cstr_t SPrintFunctionArgList (IM3Function i_function, m3stack_t i_sp) 133 | { 134 | int ret; 135 | static char string [256]; 136 | 137 | char * s = string; 138 | ccstr_t e = string + sizeof(string) - 1; 139 | 140 | ret = snprintf (s, e-s, "("); 141 | s += M3_MAX (0, ret); 142 | 143 | u64 * argSp = (u64 *) i_sp; 144 | 145 | IM3FuncType funcType = i_function->funcType; 146 | if (funcType) 147 | { 148 | u32 numArgs = funcType->numArgs; 149 | 150 | for (u32 i = 0; i < numArgs; ++i) 151 | { 152 | u8 type = d_FuncArgType(funcType, i); 153 | 154 | ret = snprintf (s, e-s, "%s: ", c_waTypes [type]); 155 | s += M3_MAX (0, ret); 156 | 157 | s += SPrintArg (s, e-s, argSp + i, type); 158 | 159 | if (i != numArgs - 1) { 160 | ret = snprintf (s, e-s, ", "); 161 | s += M3_MAX (0, ret); 162 | } 163 | } 164 | } 165 | else printf ("null signature"); 166 | 167 | ret = snprintf (s, e-s, ")"); 168 | s += M3_MAX (0, ret); 169 | 170 | return string; 171 | } 172 | 173 | static 174 | OpInfo find_operation_info (IM3Operation i_operation) 175 | { 176 | OpInfo opInfo = { NULL, 0 }; 177 | 178 | if (!i_operation) return opInfo; 179 | 180 | // TODO: find also extended opcodes 181 | for (u32 i = 0; i <= 0xff; ++i) 182 | { 183 | IM3OpInfo oi = GetOpInfo (i); 184 | 185 | if (oi->type != c_m3Type_unknown) 186 | { 187 | for (u32 o = 0; o < 4; ++o) 188 | { 189 | if (oi->operations [o] == i_operation) 190 | { 191 | opInfo.info = oi; 192 | opInfo.opcode = i; 193 | break; 194 | } 195 | } 196 | } 197 | else break; 198 | } 199 | 200 | return opInfo; 201 | } 202 | 203 | 204 | #undef fetch 205 | #define fetch(TYPE) (* (TYPE *) ((*o_pc)++)) 206 | 207 | #define d_m3Decoder(FUNC) void Decode_##FUNC (char * o_string, u8 i_opcode, IM3Operation i_operation, IM3OpInfo i_opInfo, pc_t * o_pc) 208 | 209 | d_m3Decoder (Call) 210 | { 211 | void * function = fetch (void *); 212 | i32 stackOffset = fetch (i32); 213 | 214 | sprintf (o_string, "%p; stack-offset: %d", function, stackOffset); 215 | } 216 | 217 | 218 | d_m3Decoder (Entry) 219 | { 220 | IM3Function function = fetch (IM3Function); 221 | 222 | // only prints out the first registered name for the function 223 | sprintf (o_string, "%s", m3_GetFunctionName(function)); 224 | } 225 | 226 | 227 | d_m3Decoder (f64_Store) 228 | { 229 | if (i_operation == i_opInfo->operations [0]) 230 | { 231 | u32 operand = fetch (u32); 232 | u32 offset = fetch (u32); 233 | 234 | sprintf (o_string, "offset= slot:%d + immediate:%d", operand, offset); 235 | } 236 | 237 | // sprintf (o_string, "%s", function->name); 238 | } 239 | 240 | 241 | d_m3Decoder (Branch) 242 | { 243 | void * target = fetch (void *); 244 | sprintf (o_string, "%p", target); 245 | } 246 | 247 | d_m3Decoder (BranchTable) 248 | { 249 | u32 slot = fetch (u32); 250 | 251 | o_string += sprintf (o_string, "slot: %" PRIu32 "; targets: ", slot); 252 | 253 | // IM3Function function = fetch2 (IM3Function); 254 | 255 | i32 targets = fetch (i32); 256 | 257 | for (i32 i = 0; i < targets; ++i) 258 | { 259 | pc_t addr = fetch (pc_t); 260 | o_string += sprintf (o_string, "%" PRIi32 "=%p, ", i, addr); 261 | } 262 | 263 | pc_t addr = fetch (pc_t); 264 | sprintf (o_string, "def=%p ", addr); 265 | } 266 | 267 | 268 | d_m3Decoder (Const) 269 | { 270 | u64 value = fetch (u64); i32 offset = fetch (i32); 271 | sprintf (o_string, " slot [%d] = %" PRIu64, offset, value); 272 | } 273 | 274 | 275 | #undef fetch 276 | 277 | void DecodeOperation (char * o_string, u8 i_opcode, IM3Operation i_operation, IM3OpInfo i_opInfo, pc_t * o_pc) 278 | { 279 | #define d_m3Decode(OPCODE, FUNC) case OPCODE: Decode_##FUNC (o_string, i_opcode, i_operation, i_opInfo, o_pc); break; 280 | 281 | switch (i_opcode) 282 | { 283 | // d_m3Decode (0xc0, Const) 284 | d_m3Decode (0xc5, Entry) 285 | d_m3Decode (c_waOp_call, Call) 286 | d_m3Decode (c_waOp_branch, Branch) 287 | d_m3Decode (c_waOp_branchTable, BranchTable) 288 | d_m3Decode (0x39, f64_Store) 289 | } 290 | } 291 | 292 | // WARNING/TODO: this isn't fully implemented. it blindly assumes each word is a Operation pointer 293 | // and, if an operation happens to missing from the c_operations table it won't be recognized here 294 | void dump_code_page (IM3CodePage i_codePage, pc_t i_startPC) 295 | { 296 | m3log (code, "code page seq: %d", i_codePage->info.sequence); 297 | 298 | pc_t pc = i_startPC ? i_startPC : GetPageStartPC (i_codePage); 299 | pc_t end = GetPagePC (i_codePage); 300 | 301 | m3log (code, "---------------------------------------------------------------------------------------"); 302 | 303 | while (pc < end) 304 | { 305 | pc_t operationPC = pc; 306 | IM3Operation op = (IM3Operation) (* pc++); 307 | 308 | OpInfo i = find_operation_info (op); 309 | 310 | if (i.info) 311 | { 312 | char infoString [8*1024] = { 0 }; 313 | 314 | DecodeOperation (infoString, i.opcode, op, i.info, & pc); 315 | 316 | m3log (code, "%p | %20s %s", operationPC, i.info->name, infoString); 317 | } 318 | else 319 | m3log (code, "%p | %p", operationPC, op); 320 | 321 | } 322 | 323 | m3log (code, "---------------------------------------------------------------------------------------"); 324 | 325 | m3log (code, "free-lines: %d", i_codePage->info.numLines - i_codePage->info.lineIndex); 326 | } 327 | 328 | 329 | void dump_type_stack (IM3Compilation o) 330 | { 331 | /* Reminders about how the stack works! :) 332 | -- args & locals remain on the type stack for duration of the function. Denoted with a constant 'A' and 'L' in this dump. 333 | -- the initial stack dumps originate from the CompileLocals () function, so these identifiers won't/can't be 334 | applied until this compilation stage is finished 335 | -- constants are not statically represented in the type stack (like args & constants) since they don't have/need 336 | write counts 337 | 338 | -- the number shown for static args and locals (value in wasmStack [i]) represents the write count for the variable 339 | 340 | -- (does Wasm ever write to an arg? I dunno/don't remember.) 341 | -- the number for the dynamic stack values represents the slot number. 342 | -- if the slot index points to arg, local or constant it's denoted with a lowercase 'a', 'l' or 'c' 343 | 344 | */ 345 | 346 | // for the assert at end of dump: 347 | i32 regAllocated [2] = { (i32) IsRegisterAllocated (o, 0), (i32) IsRegisterAllocated (o, 1) }; 348 | 349 | // display whether r0 or fp0 is allocated. these should then also be reflected somewhere in the stack too. 350 | d_m3Log(stack, "\n"); 351 | d_m3Log(stack, " "); 352 | printf ("%s %s ", regAllocated [0] ? "(r0)" : " ", regAllocated [1] ? "(fp0)" : " "); 353 | printf("\n"); 354 | 355 | for (u32 p = 1; p <= 2; ++p) 356 | { 357 | d_m3Log(stack, " "); 358 | 359 | for (u32 i = 0; i < o->stackIndex; ++i) 360 | { 361 | if (i > 0 and i == o->stackFirstDynamicIndex) 362 | printf ("#"); 363 | 364 | if (i == o->block.blockStackIndex) 365 | printf (">"); 366 | 367 | const char * type = c_waCompactTypes [o->typeStack [i]]; 368 | 369 | const char * location = ""; 370 | 371 | i32 slot = o->wasmStack [i]; 372 | 373 | if (IsRegisterSlotAlias (slot)) 374 | { 375 | bool isFp = IsFpRegisterSlotAlias (slot); 376 | location = isFp ? "/f" : "/r"; 377 | 378 | regAllocated [isFp]--; 379 | slot = -1; 380 | } 381 | else 382 | { 383 | if (slot < o->slotFirstDynamicIndex) 384 | { 385 | if (slot >= o->slotFirstConstIndex) 386 | location = "c"; 387 | else if (slot >= o->function->numRetAndArgSlots) 388 | location = "L"; 389 | else 390 | location = "a"; 391 | } 392 | } 393 | 394 | char item [100]; 395 | 396 | if (slot >= 0) 397 | sprintf (item, "%s%s%d", type, location, slot); 398 | else 399 | sprintf (item, "%s%s", type, location); 400 | 401 | if (p == 1) 402 | { 403 | size_t s = strlen (item); 404 | 405 | sprintf (item, "%d", i); 406 | 407 | while (strlen (item) < s) 408 | strcat (item, " "); 409 | } 410 | 411 | printf ("|%s ", item); 412 | 413 | } 414 | printf ("\n"); 415 | } 416 | 417 | // for (u32 r = 0; r < 2; ++r) 418 | // d_m3Assert (regAllocated [r] == 0); // reg allocation & stack out of sync 419 | 420 | u16 maxSlot = GetMaxUsedSlotPlusOne (o); 421 | 422 | if (maxSlot > o->slotFirstDynamicIndex) 423 | { 424 | d_m3Log (stack, " -"); 425 | 426 | for (u16 i = o->slotFirstDynamicIndex; i < maxSlot; ++i) 427 | printf ("----"); 428 | 429 | printf ("\n"); 430 | 431 | d_m3Log (stack, " slot |"); 432 | for (u16 i = o->slotFirstDynamicIndex; i < maxSlot; ++i) 433 | printf ("%3d|", i); 434 | 435 | printf ("\n"); 436 | d_m3Log (stack, " alloc |"); 437 | 438 | for (u16 i = o->slotFirstDynamicIndex; i < maxSlot; ++i) 439 | { 440 | printf ("%3d|", o->m3Slots [i]); 441 | } 442 | 443 | printf ("\n"); 444 | } 445 | d_m3Log(stack, "\n"); 446 | } 447 | 448 | 449 | static const char * GetOpcodeIndentionString (i32 blockDepth) 450 | { 451 | blockDepth += 1; 452 | 453 | if (blockDepth < 0) 454 | blockDepth = 0; 455 | 456 | static const char * s_spaces = "......................................................................................."; 457 | const char * indent = s_spaces + strlen (s_spaces); 458 | indent -= (blockDepth * 2); 459 | if (indent < s_spaces) 460 | indent = s_spaces; 461 | 462 | return indent; 463 | } 464 | 465 | 466 | const char * get_indention_string (IM3Compilation o) 467 | { 468 | return GetOpcodeIndentionString (o->block.depth+4); 469 | } 470 | 471 | 472 | void log_opcode (IM3Compilation o, m3opcode_t i_opcode) 473 | { 474 | i32 depth = o->block.depth; 475 | if (i_opcode == c_waOp_end or i_opcode == c_waOp_else) 476 | depth--; 477 | 478 | m3log (compile, "%4d | 0x%02x %s %s", o->numOpcodes++, i_opcode, GetOpcodeIndentionString (depth), GetOpInfo(i_opcode)->name); 479 | } 480 | 481 | 482 | void log_emit (IM3Compilation o, IM3Operation i_operation) 483 | { 484 | OpInfo i = find_operation_info (i_operation); 485 | 486 | d_m3Log(emit, ""); 487 | if (i.info) 488 | { 489 | printf ("%p: %s\n", GetPagePC (o->page), i.info->name); 490 | } 491 | else printf ("not found: %p\n", i_operation); 492 | } 493 | 494 | #endif // DEBUG 495 | 496 | 497 | # if d_m3EnableOpProfiling 498 | 499 | typedef struct M3ProfilerSlot 500 | { 501 | cstr_t opName; 502 | u64 hitCount; 503 | } 504 | M3ProfilerSlot; 505 | 506 | static M3ProfilerSlot s_opProfilerCounts [d_m3ProfilerSlotMask + 1] = {}; 507 | 508 | void ProfileHit (cstr_t i_operationName) 509 | { 510 | u64 ptr = (u64) i_operationName; 511 | 512 | M3ProfilerSlot * slot = & s_opProfilerCounts [ptr & d_m3ProfilerSlotMask]; 513 | 514 | if (slot->opName) 515 | { 516 | if (slot->opName != i_operationName) 517 | { 518 | m3_Abort ("profiler slot collision; increase d_m3ProfilerSlotMask"); 519 | } 520 | } 521 | 522 | slot->opName = i_operationName; 523 | slot->hitCount++; 524 | } 525 | 526 | 527 | void m3_PrintProfilerInfo () 528 | { 529 | M3ProfilerSlot dummy; 530 | M3ProfilerSlot * maxSlot = & dummy; 531 | 532 | do 533 | { 534 | maxSlot->hitCount = 0; 535 | 536 | for (u32 i = 0; i <= d_m3ProfilerSlotMask; ++i) 537 | { 538 | M3ProfilerSlot * slot = & s_opProfilerCounts [i]; 539 | 540 | if (slot->opName) 541 | { 542 | if (slot->hitCount > maxSlot->hitCount) 543 | maxSlot = slot; 544 | } 545 | } 546 | 547 | if (maxSlot->opName) 548 | { 549 | fprintf (stderr, "%13llu %s\n", maxSlot->hitCount, maxSlot->opName); 550 | maxSlot->opName = NULL; 551 | } 552 | } 553 | while (maxSlot->hitCount); 554 | } 555 | 556 | # else 557 | 558 | void m3_PrintProfilerInfo () {} 559 | 560 | # endif 561 | 562 | -------------------------------------------------------------------------------- /wasm3/m3_info.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_info.h 3 | // 4 | // Created by Steven Massey on 12/6/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | 8 | #ifndef m3_info_h 9 | #define m3_info_h 10 | 11 | #include "m3_compile.h" 12 | 13 | d_m3BeginExternC 14 | 15 | void ProfileHit (cstr_t i_operationName); 16 | 17 | #ifdef DEBUG 18 | 19 | void dump_type_stack (IM3Compilation o); 20 | void log_opcode (IM3Compilation o, m3opcode_t i_opcode); 21 | const char * get_indention_string (IM3Compilation o); 22 | void log_emit (IM3Compilation o, IM3Operation i_operation); 23 | 24 | cstr_t SPrintFuncTypeSignature (IM3FuncType i_funcType); 25 | 26 | #else // DEBUG 27 | 28 | #define dump_type_stack(...) {} 29 | #define log_opcode(...) {} 30 | #define get_indention_string(...) "" 31 | #define emit_stack_dump(...) {} 32 | #define log_emit(...) {} 33 | 34 | #endif // DEBUG 35 | 36 | d_m3EndExternC 37 | 38 | #endif // m3_info_h 39 | -------------------------------------------------------------------------------- /wasm3/m3_math_utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_math_utils.h 3 | // 4 | // Created by Volodymyr Shymanksyy on 8/10/19. 5 | // Copyright © 2019 Volodymyr Shymanskyy. All rights reserved. 6 | // 7 | 8 | #ifndef m3_math_utils_h 9 | #define m3_math_utils_h 10 | 11 | #include "m3_core.h" 12 | 13 | #include 14 | 15 | #if defined(M3_COMPILER_MSVC) 16 | 17 | #include 18 | 19 | #define __builtin_popcount __popcnt 20 | 21 | static inline 22 | int __builtin_ctz(uint32_t x) { 23 | unsigned long ret; 24 | _BitScanForward(&ret, x); 25 | return (int)ret; 26 | } 27 | 28 | static inline 29 | int __builtin_clz(uint32_t x) { 30 | unsigned long ret; 31 | _BitScanReverse(&ret, x); 32 | return (int)(31 ^ ret); 33 | } 34 | 35 | 36 | 37 | #ifdef _WIN64 38 | 39 | #define __builtin_popcountll __popcnt64 40 | 41 | static inline 42 | int __builtin_ctzll(uint64_t value) { 43 | unsigned long ret; 44 | _BitScanForward64(&ret, value); 45 | return (int)ret; 46 | } 47 | 48 | static inline 49 | int __builtin_clzll(uint64_t value) { 50 | unsigned long ret; 51 | _BitScanReverse64(&ret, value); 52 | return (int)(63 ^ ret); 53 | } 54 | 55 | #else // _WIN64 56 | 57 | #define __builtin_popcountll(x) (__popcnt((x) & 0xFFFFFFFF) + __popcnt((x) >> 32)) 58 | 59 | static inline 60 | int __builtin_ctzll(uint64_t value) { 61 | //if (value == 0) return 64; // Note: ctz(0) result is undefined anyway 62 | uint32_t msh = (uint32_t)(value >> 32); 63 | uint32_t lsh = (uint32_t)(value & 0xFFFFFFFF); 64 | if (lsh != 0) return __builtin_ctz(lsh); 65 | return 32 + __builtin_ctz(msh); 66 | } 67 | 68 | static inline 69 | int __builtin_clzll(uint64_t value) { 70 | //if (value == 0) return 64; // Note: clz(0) result is undefined anyway 71 | uint32_t msh = (uint32_t)(value >> 32); 72 | uint32_t lsh = (uint32_t)(value & 0xFFFFFFFF); 73 | if (msh != 0) return __builtin_clz(msh); 74 | return 32 + __builtin_clz(lsh); 75 | } 76 | 77 | #endif // _WIN64 78 | 79 | #endif // defined(M3_COMPILER_MSVC) 80 | 81 | 82 | // TODO: not sure why, signbit is actually defined in math.h 83 | #if (defined(ESP8266) || defined(ESP32)) && !defined(signbit) 84 | #define signbit(__x) \ 85 | ((sizeof(__x) == sizeof(float)) ? __signbitf(__x) : __signbitd(__x)) 86 | #endif 87 | 88 | #if defined(__AVR__) 89 | 90 | static inline 91 | float rintf( float arg ) { 92 | union { float f; uint32_t i; } u; 93 | u.f = arg; 94 | uint32_t ux = u.i & 0x7FFFFFFF; 95 | if (M3_UNLIKELY(ux == 0 || ux > 0x5A000000)) { 96 | return arg; 97 | } 98 | return (float)lrint(arg); 99 | } 100 | 101 | static inline 102 | double rint( double arg ) { 103 | union { double f; uint32_t i[2]; } u; 104 | u.f = arg; 105 | uint32_t ux = u.i[1] & 0x7FFFFFFF; 106 | if (M3_UNLIKELY((ux == 0 && u.i[0] == 0) || ux > 0x433FFFFF)) { 107 | return arg; 108 | } 109 | return (double)lrint(arg); 110 | } 111 | 112 | //TODO 113 | static inline 114 | uint64_t strtoull(const char* str, char** endptr, int base) { 115 | return 0; 116 | } 117 | 118 | #endif 119 | 120 | /* 121 | * Rotr, Rotl 122 | */ 123 | 124 | static inline 125 | u32 rotl32(u32 n, unsigned c) { 126 | const unsigned mask = CHAR_BIT * sizeof(n) - 1; 127 | c &= mask & 31; 128 | return (n << c) | (n >> ((-c) & mask)); 129 | } 130 | 131 | static inline 132 | u32 rotr32(u32 n, unsigned c) { 133 | const unsigned mask = CHAR_BIT * sizeof(n) - 1; 134 | c &= mask & 31; 135 | return (n >> c) | (n << ((-c) & mask)); 136 | } 137 | 138 | static inline 139 | u64 rotl64(u64 n, unsigned c) { 140 | const unsigned mask = CHAR_BIT * sizeof(n) - 1; 141 | c &= mask & 63; 142 | return (n << c) | (n >> ((-c) & mask)); 143 | } 144 | 145 | static inline 146 | u64 rotr64(u64 n, unsigned c) { 147 | const unsigned mask = CHAR_BIT * sizeof(n) - 1; 148 | c &= mask & 63; 149 | return (n >> c) | (n << ((-c) & mask)); 150 | } 151 | 152 | /* 153 | * Integer Div, Rem 154 | */ 155 | 156 | #define OP_DIV_U(RES, A, B) \ 157 | if (M3_UNLIKELY(B == 0)) newTrap (m3Err_trapDivisionByZero); \ 158 | RES = A / B; 159 | 160 | #define OP_REM_U(RES, A, B) \ 161 | if (M3_UNLIKELY(B == 0)) newTrap (m3Err_trapDivisionByZero); \ 162 | RES = A % B; 163 | 164 | // 2's complement detection 165 | #if (INT_MIN != -INT_MAX) 166 | 167 | #define OP_DIV_S(RES, A, B, TYPE_MIN) \ 168 | if (M3_UNLIKELY(B == 0)) newTrap (m3Err_trapDivisionByZero); \ 169 | if (M3_UNLIKELY(B == -1 and A == TYPE_MIN)) { \ 170 | newTrap (m3Err_trapIntegerOverflow); \ 171 | } \ 172 | RES = A / B; 173 | 174 | #define OP_REM_S(RES, A, B, TYPE_MIN) \ 175 | if (M3_UNLIKELY(B == 0)) newTrap (m3Err_trapDivisionByZero); \ 176 | if (M3_UNLIKELY(B == -1 and A == TYPE_MIN)) RES = 0; \ 177 | else RES = A % B; 178 | 179 | #else 180 | 181 | #define OP_DIV_S(RES, A, B, TYPE_MIN) OP_DIV_U(RES, A, B) 182 | #define OP_REM_S(RES, A, B, TYPE_MIN) OP_REM_U(RES, A, B) 183 | 184 | #endif 185 | 186 | /* 187 | * Trunc 188 | */ 189 | 190 | #define OP_TRUNC(RES, A, TYPE, RMIN, RMAX) \ 191 | if (M3_UNLIKELY(isnan(A))) { \ 192 | newTrap (m3Err_trapIntegerConversion); \ 193 | } \ 194 | if (M3_UNLIKELY(A <= RMIN or A >= RMAX)) { \ 195 | newTrap (m3Err_trapIntegerOverflow); \ 196 | } \ 197 | RES = (TYPE)A; 198 | 199 | 200 | #define OP_I32_TRUNC_F32(RES, A) OP_TRUNC(RES, A, i32, -2147483904.0f, 2147483648.0f) 201 | #define OP_U32_TRUNC_F32(RES, A) OP_TRUNC(RES, A, u32, -1.0f, 4294967296.0f) 202 | #define OP_I32_TRUNC_F64(RES, A) OP_TRUNC(RES, A, i32, -2147483649.0 , 2147483648.0 ) 203 | #define OP_U32_TRUNC_F64(RES, A) OP_TRUNC(RES, A, u32, -1.0 , 4294967296.0 ) 204 | 205 | #define OP_I64_TRUNC_F32(RES, A) OP_TRUNC(RES, A, i64, -9223373136366403584.0f, 9223372036854775808.0f) 206 | #define OP_U64_TRUNC_F32(RES, A) OP_TRUNC(RES, A, u64, -1.0f, 18446744073709551616.0f) 207 | #define OP_I64_TRUNC_F64(RES, A) OP_TRUNC(RES, A, i64, -9223372036854777856.0 , 9223372036854775808.0 ) 208 | #define OP_U64_TRUNC_F64(RES, A) OP_TRUNC(RES, A, u64, -1.0 , 18446744073709551616.0 ) 209 | 210 | #define OP_TRUNC_SAT(RES, A, TYPE, RMIN, RMAX, IMIN, IMAX) \ 211 | if (M3_UNLIKELY(isnan(A))) { \ 212 | RES = 0; \ 213 | } else if (M3_UNLIKELY(A <= RMIN)) { \ 214 | RES = IMIN; \ 215 | } else if (M3_UNLIKELY(A >= RMAX)) { \ 216 | RES = IMAX; \ 217 | } else { \ 218 | RES = (TYPE)A; \ 219 | } 220 | 221 | #define OP_I32_TRUNC_SAT_F32(RES, A) OP_TRUNC_SAT(RES, A, i32, -2147483904.0f, 2147483648.0f, INT32_MIN, INT32_MAX) 222 | #define OP_U32_TRUNC_SAT_F32(RES, A) OP_TRUNC_SAT(RES, A, u32, -1.0f, 4294967296.0f, 0UL, UINT32_MAX) 223 | #define OP_I32_TRUNC_SAT_F64(RES, A) OP_TRUNC_SAT(RES, A, i32, -2147483649.0 , 2147483648.0, INT32_MIN, INT32_MAX) 224 | #define OP_U32_TRUNC_SAT_F64(RES, A) OP_TRUNC_SAT(RES, A, u32, -1.0 , 4294967296.0, 0UL, UINT32_MAX) 225 | 226 | #define OP_I64_TRUNC_SAT_F32(RES, A) OP_TRUNC_SAT(RES, A, i64, -9223373136366403584.0f, 9223372036854775808.0f, INT64_MIN, INT64_MAX) 227 | #define OP_U64_TRUNC_SAT_F32(RES, A) OP_TRUNC_SAT(RES, A, u64, -1.0f, 18446744073709551616.0f, 0ULL, UINT64_MAX) 228 | #define OP_I64_TRUNC_SAT_F64(RES, A) OP_TRUNC_SAT(RES, A, i64, -9223372036854777856.0 , 9223372036854775808.0, INT64_MIN, INT64_MAX) 229 | #define OP_U64_TRUNC_SAT_F64(RES, A) OP_TRUNC_SAT(RES, A, u64, -1.0 , 18446744073709551616.0, 0ULL, UINT64_MAX) 230 | 231 | /* 232 | * Min, Max 233 | */ 234 | 235 | #if d_m3HasFloat 236 | 237 | #include 238 | 239 | static inline 240 | f32 min_f32(f32 a, f32 b) { 241 | if (M3_UNLIKELY(isnan(a) or isnan(b))) return NAN; 242 | if (M3_UNLIKELY(a == 0 and a == b)) return signbit(a) ? a : b; 243 | return a > b ? b : a; 244 | } 245 | 246 | static inline 247 | f32 max_f32(f32 a, f32 b) { 248 | if (M3_UNLIKELY(isnan(a) or isnan(b))) return NAN; 249 | if (M3_UNLIKELY(a == 0 and a == b)) return signbit(a) ? b : a; 250 | return a > b ? a : b; 251 | } 252 | 253 | static inline 254 | f64 min_f64(f64 a, f64 b) { 255 | if (M3_UNLIKELY(isnan(a) or isnan(b))) return NAN; 256 | if (M3_UNLIKELY(a == 0 and a == b)) return signbit(a) ? a : b; 257 | return a > b ? b : a; 258 | } 259 | 260 | static inline 261 | f64 max_f64(f64 a, f64 b) { 262 | if (M3_UNLIKELY(isnan(a) or isnan(b))) return NAN; 263 | if (M3_UNLIKELY(a == 0 and a == b)) return signbit(a) ? b : a; 264 | return a > b ? a : b; 265 | } 266 | #endif 267 | 268 | #endif // m3_math_utils_h 269 | -------------------------------------------------------------------------------- /wasm3/m3_module.c: -------------------------------------------------------------------------------- 1 | // 2 | // m3_module.c 3 | // 4 | // Created by Steven Massey on 5/7/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | 8 | #include "m3_env.h" 9 | #include "m3_exception.h" 10 | 11 | 12 | void Module_FreeFunctions (IM3Module i_module) 13 | { 14 | for (u32 i = 0; i < i_module->numFunctions; ++i) 15 | { 16 | IM3Function func = & i_module->functions [i]; 17 | Function_Release (func); 18 | } 19 | } 20 | 21 | 22 | void m3_FreeModule (IM3Module i_module) 23 | { 24 | if (i_module) 25 | { 26 | m3log (module, "freeing module: %s (funcs: %d; segments: %d)", 27 | i_module->name, i_module->numFunctions, i_module->numDataSegments); 28 | 29 | Module_FreeFunctions (i_module); 30 | 31 | m3_Free (i_module->functions); 32 | //m3_Free (i_module->imports); 33 | m3_Free (i_module->funcTypes); 34 | m3_Free (i_module->dataSegments); 35 | m3_Free (i_module->table0); 36 | 37 | for (u32 i = 0; i < i_module->numGlobals; ++i) 38 | { 39 | m3_Free (i_module->globals[i].name); 40 | FreeImportInfo(&(i_module->globals[i].import)); 41 | } 42 | m3_Free (i_module->globals); 43 | 44 | m3_Free (i_module); 45 | } 46 | } 47 | 48 | 49 | M3Result Module_AddGlobal (IM3Module io_module, IM3Global * o_global, u8 i_type, bool i_mutable, bool i_isImported) 50 | { 51 | _try { 52 | u32 index = io_module->numGlobals++; 53 | io_module->globals = m3_ReallocArray (M3Global, io_module->globals, io_module->numGlobals, index); 54 | _throwifnull (io_module->globals); 55 | M3Global * global = & io_module->globals [index]; 56 | 57 | global->type = i_type; 58 | global->imported = i_isImported; 59 | global->isMutable = i_mutable; 60 | 61 | if (o_global) 62 | * o_global = global; 63 | 64 | } _catch: 65 | return result; 66 | } 67 | 68 | M3Result Module_PreallocFunctions (IM3Module io_module, u32 i_totalFunctions) 69 | { 70 | _try { 71 | if (i_totalFunctions > io_module->allFunctions) { 72 | io_module->functions = m3_ReallocArray (M3Function, io_module->functions, i_totalFunctions, io_module->allFunctions); 73 | io_module->allFunctions = i_totalFunctions; 74 | _throwifnull (io_module->functions); 75 | } 76 | } _catch: 77 | return result; 78 | } 79 | 80 | M3Result Module_AddFunction (IM3Module io_module, u32 i_typeIndex, IM3ImportInfo i_importInfo) 81 | { 82 | _try { 83 | 84 | u32 index = io_module->numFunctions++; 85 | _ (Module_PreallocFunctions(io_module, io_module->numFunctions)); 86 | 87 | _throwif ("type sig index out of bounds", i_typeIndex >= io_module->numFuncTypes); 88 | 89 | IM3FuncType ft = io_module->funcTypes [i_typeIndex]; 90 | 91 | IM3Function func = Module_GetFunction (io_module, index); 92 | func->funcType = ft; 93 | 94 | # ifdef DEBUG 95 | func->index = index; 96 | # endif 97 | 98 | if (i_importInfo and func->numNames == 0) 99 | { 100 | func->import = * i_importInfo; 101 | func->names[0] = i_importInfo->fieldUtf8; 102 | func->numNames = 1; 103 | } 104 | 105 | m3log (module, " added function: %3d; sig: %d", index, i_typeIndex); 106 | 107 | } _catch: 108 | return result; 109 | } 110 | 111 | #ifdef DEBUG 112 | void Module_GenerateNames (IM3Module i_module) 113 | { 114 | for (u32 i = 0; i < i_module->numFunctions; ++i) 115 | { 116 | IM3Function func = & i_module->functions [i]; 117 | 118 | if (func->numNames == 0) 119 | { 120 | char* buff = m3_AllocArray(char, 16); 121 | snprintf(buff, 16, "$func%d", i); 122 | func->names[0] = buff; 123 | func->numNames = 1; 124 | } 125 | } 126 | for (u32 i = 0; i < i_module->numGlobals; ++i) 127 | { 128 | IM3Global global = & i_module->globals [i]; 129 | 130 | if (global->name == NULL) 131 | { 132 | char* buff = m3_AllocArray(char, 16); 133 | snprintf(buff, 16, "$global%d", i); 134 | global->name = buff; 135 | } 136 | } 137 | } 138 | #endif 139 | 140 | IM3Function Module_GetFunction (IM3Module i_module, u32 i_functionIndex) 141 | { 142 | IM3Function func = NULL; 143 | 144 | if (i_functionIndex < i_module->numFunctions) 145 | { 146 | func = & i_module->functions [i_functionIndex]; 147 | //func->module = i_module; 148 | } 149 | 150 | return func; 151 | } 152 | 153 | 154 | const char* m3_GetModuleName (IM3Module i_module) 155 | { 156 | if (!i_module || !i_module->name) 157 | return ".unnamed"; 158 | 159 | return i_module->name; 160 | } 161 | 162 | void m3_SetModuleName (IM3Module i_module, const char* name) 163 | { 164 | if (i_module) i_module->name = name; 165 | } 166 | 167 | IM3Runtime m3_GetModuleRuntime (IM3Module i_module) 168 | { 169 | return i_module ? i_module->runtime : NULL; 170 | } 171 | 172 | -------------------------------------------------------------------------------- /wasm3/wasm3.h: -------------------------------------------------------------------------------- 1 | // 2 | // Wasm3, high performance WebAssembly interpreter 3 | // 4 | // Copyright © 2019 Steven Massey, Volodymyr Shymanskyy. 5 | // All rights reserved. 6 | // 7 | 8 | #ifndef wasm3_h 9 | #define wasm3_h 10 | 11 | #define M3_VERSION_MAJOR 0 12 | #define M3_VERSION_MINOR 5 13 | #define M3_VERSION_REV 0 14 | #define M3_VERSION "0.5.0" 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "wasm3_defs.h" 22 | 23 | // Constants 24 | #define M3_BACKTRACE_TRUNCATED (void*)(SIZE_MAX) 25 | 26 | #if defined(__cplusplus) 27 | extern "C" { 28 | #endif 29 | 30 | typedef const char * M3Result; 31 | 32 | struct M3Environment; typedef struct M3Environment * IM3Environment; 33 | struct M3Runtime; typedef struct M3Runtime * IM3Runtime; 34 | struct M3Module; typedef struct M3Module * IM3Module; 35 | struct M3Function; typedef struct M3Function * IM3Function; 36 | struct M3Global; typedef struct M3Global * IM3Global; 37 | 38 | typedef struct M3ErrorInfo 39 | { 40 | M3Result result; 41 | 42 | IM3Runtime runtime; 43 | IM3Module module; 44 | IM3Function function; 45 | 46 | const char * file; 47 | uint32_t line; 48 | 49 | const char * message; 50 | } M3ErrorInfo; 51 | 52 | typedef struct M3BacktraceFrame 53 | { 54 | uint32_t moduleOffset; 55 | IM3Function function; 56 | 57 | struct M3BacktraceFrame * next; 58 | } 59 | M3BacktraceFrame, * IM3BacktraceFrame; 60 | 61 | typedef struct M3BacktraceInfo 62 | { 63 | IM3BacktraceFrame frames; 64 | IM3BacktraceFrame lastFrame; // can be M3_BACKTRACE_TRUNCATED 65 | } 66 | M3BacktraceInfo, * IM3BacktraceInfo; 67 | 68 | 69 | typedef enum M3ValueType 70 | { 71 | c_m3Type_none = 0, 72 | c_m3Type_i32 = 1, 73 | c_m3Type_i64 = 2, 74 | c_m3Type_f32 = 3, 75 | c_m3Type_f64 = 4, 76 | 77 | c_m3Type_unknown 78 | } M3ValueType; 79 | 80 | typedef struct M3TaggedValue 81 | { 82 | M3ValueType type; 83 | union M3ValueUnion 84 | { 85 | uint32_t i32; 86 | uint64_t i64; 87 | float f32; 88 | double f64; 89 | } value; 90 | } 91 | M3TaggedValue, * IM3TaggedValue; 92 | 93 | typedef struct M3ImportInfo 94 | { 95 | const char * moduleUtf8; 96 | const char * fieldUtf8; 97 | } 98 | M3ImportInfo, * IM3ImportInfo; 99 | 100 | 101 | typedef struct M3ImportContext 102 | { 103 | void * userdata; 104 | IM3Function function; 105 | } 106 | M3ImportContext, * IM3ImportContext; 107 | 108 | // ------------------------------------------------------------------------------------------------------------------------------- 109 | // error codes 110 | // ------------------------------------------------------------------------------------------------------------------------------- 111 | 112 | # if defined(M3_IMPLEMENT_ERROR_STRINGS) 113 | # define d_m3ErrorConst(LABEL, STRING) extern const M3Result m3Err_##LABEL = { STRING }; 114 | # else 115 | # define d_m3ErrorConst(LABEL, STRING) extern const M3Result m3Err_##LABEL; 116 | # endif 117 | 118 | // ------------------------------------------------------------------------------------------------------------------------------- 119 | 120 | d_m3ErrorConst (none, NULL) 121 | 122 | // general errors 123 | d_m3ErrorConst (mallocFailed, "memory allocation failed") 124 | 125 | // parse errors 126 | d_m3ErrorConst (incompatibleWasmVersion, "incompatible Wasm binary version") 127 | d_m3ErrorConst (wasmMalformed, "malformed Wasm binary") 128 | d_m3ErrorConst (misorderedWasmSection, "out of order Wasm section") 129 | d_m3ErrorConst (wasmUnderrun, "underrun while parsing Wasm binary") 130 | d_m3ErrorConst (wasmOverrun, "overrun while parsing Wasm binary") 131 | d_m3ErrorConst (wasmMissingInitExpr, "missing init_expr in Wasm binary") 132 | d_m3ErrorConst (lebOverflow, "LEB encoded value overflow") 133 | d_m3ErrorConst (missingUTF8, "invalid length UTF-8 string") 134 | d_m3ErrorConst (wasmSectionUnderrun, "section underrun while parsing Wasm binary") 135 | d_m3ErrorConst (wasmSectionOverrun, "section overrun while parsing Wasm binary") 136 | d_m3ErrorConst (invalidTypeId, "unknown value_type") 137 | d_m3ErrorConst (tooManyMemorySections, "only one memory per module is supported") 138 | d_m3ErrorConst (tooManyArgsRets, "too many arguments or return values") 139 | 140 | // link errors 141 | d_m3ErrorConst (moduleNotLinked, "attempting to use module that is not loaded") 142 | d_m3ErrorConst (moduleAlreadyLinked, "attempting to bind module to multiple runtimes") 143 | d_m3ErrorConst (functionLookupFailed, "function lookup failed") 144 | d_m3ErrorConst (functionImportMissing, "missing imported function") 145 | 146 | d_m3ErrorConst (malformedFunctionSignature, "malformed function signature") 147 | 148 | // compilation errors 149 | d_m3ErrorConst (noCompiler, "no compiler found for opcode") 150 | d_m3ErrorConst (unknownOpcode, "unknown opcode") 151 | d_m3ErrorConst (restrictedOpcode, "restricted opcode") 152 | d_m3ErrorConst (functionStackOverflow, "compiling function overran its stack height limit") 153 | d_m3ErrorConst (functionStackUnderrun, "compiling function underran the stack") 154 | d_m3ErrorConst (mallocFailedCodePage, "memory allocation failed when acquiring a new M3 code page") 155 | d_m3ErrorConst (settingImmutableGlobal, "attempting to set an immutable global") 156 | d_m3ErrorConst (typeMismatch, "incorrect type on stack") 157 | d_m3ErrorConst (typeCountMismatch, "incorrect value count on stack") 158 | 159 | // runtime errors 160 | d_m3ErrorConst (missingCompiledCode, "function is missing compiled m3 code") 161 | d_m3ErrorConst (wasmMemoryOverflow, "runtime ran out of memory") 162 | d_m3ErrorConst (globalMemoryNotAllocated, "global memory is missing from a module") 163 | d_m3ErrorConst (globaIndexOutOfBounds, "global index is too large") 164 | d_m3ErrorConst (argumentCountMismatch, "argument count mismatch") 165 | d_m3ErrorConst (argumentTypeMismatch, "argument type mismatch") 166 | d_m3ErrorConst (globalLookupFailed, "global lookup failed") 167 | d_m3ErrorConst (globalTypeMismatch, "global type mismatch") 168 | d_m3ErrorConst (globalNotMutable, "global is not mutable") 169 | 170 | // traps 171 | d_m3ErrorConst (trapOutOfBoundsMemoryAccess, "[trap] out of bounds memory access") 172 | d_m3ErrorConst (trapDivisionByZero, "[trap] integer divide by zero") 173 | d_m3ErrorConst (trapIntegerOverflow, "[trap] integer overflow") 174 | d_m3ErrorConst (trapIntegerConversion, "[trap] invalid conversion to integer") 175 | d_m3ErrorConst (trapIndirectCallTypeMismatch, "[trap] indirect call type mismatch") 176 | d_m3ErrorConst (trapTableIndexOutOfRange, "[trap] undefined element") 177 | d_m3ErrorConst (trapTableElementIsNull, "[trap] null table element") 178 | d_m3ErrorConst (trapExit, "[trap] program called exit") 179 | d_m3ErrorConst (trapAbort, "[trap] program called abort") 180 | d_m3ErrorConst (trapUnreachable, "[trap] unreachable executed") 181 | d_m3ErrorConst (trapStackOverflow, "[trap] stack overflow") 182 | 183 | 184 | //------------------------------------------------------------------------------------------------------------------------------- 185 | // configuration, can be found in m3_config.h, m3_config_platforms.h, m3_core.h) 186 | //------------------------------------------------------------------------------------------------------------------------------- 187 | 188 | //------------------------------------------------------------------------------------------------------------------------------- 189 | // global environment than can host multiple runtimes 190 | //------------------------------------------------------------------------------------------------------------------------------- 191 | IM3Environment m3_NewEnvironment (void); 192 | 193 | void m3_FreeEnvironment (IM3Environment i_environment); 194 | 195 | typedef M3Result (* M3SectionHandler) (IM3Module i_module, const char* name, const uint8_t * start, const uint8_t * end); 196 | 197 | void m3_SetCustomSectionHandler (IM3Environment i_environment, M3SectionHandler i_handler); 198 | 199 | 200 | //------------------------------------------------------------------------------------------------------------------------------- 201 | // execution context 202 | //------------------------------------------------------------------------------------------------------------------------------- 203 | 204 | IM3Runtime m3_NewRuntime (IM3Environment io_environment, 205 | uint32_t i_stackSizeInBytes, 206 | void * i_userdata); 207 | 208 | void m3_FreeRuntime (IM3Runtime i_runtime); 209 | 210 | // Wasm currently only supports one memory region. i_memoryIndex should be zero. 211 | uint8_t * m3_GetMemory (IM3Runtime i_runtime, 212 | uint32_t * o_memorySizeInBytes, 213 | uint32_t i_memoryIndex); 214 | 215 | // This is used internally by Raw Function helpers 216 | uint32_t m3_GetMemorySize (IM3Runtime i_runtime); 217 | 218 | void * m3_GetUserData (IM3Runtime i_runtime); 219 | 220 | 221 | //------------------------------------------------------------------------------------------------------------------------------- 222 | // modules 223 | //------------------------------------------------------------------------------------------------------------------------------- 224 | 225 | // i_wasmBytes data must be persistent during the lifetime of the module 226 | M3Result m3_ParseModule (IM3Environment i_environment, 227 | IM3Module * o_module, 228 | const uint8_t * const i_wasmBytes, 229 | uint32_t i_numWasmBytes); 230 | 231 | // Only modules not loaded into a M3Runtime need to be freed. A module is considered unloaded if 232 | // a. m3_LoadModule has not yet been called on that module. Or, 233 | // b. m3_LoadModule returned a result. 234 | void m3_FreeModule (IM3Module i_module); 235 | 236 | // LoadModule transfers ownership of a module to the runtime. Do not free modules once successfully loaded into the runtime 237 | M3Result m3_LoadModule (IM3Runtime io_runtime, IM3Module io_module); 238 | 239 | // Optional, compiles all functions in the module 240 | M3Result m3_CompileModule (IM3Module io_module); 241 | 242 | // Calling m3_RunStart is optional 243 | M3Result m3_RunStart (IM3Module i_module); 244 | 245 | // Arguments and return values are passed in and out through the stack pointer _sp. 246 | // Placeholder return value slots are first and arguments after. So, the first argument is at _sp [numReturns] 247 | // Return values should be written into _sp [0] to _sp [num_returns - 1] 248 | typedef const void * (* M3RawCall) (IM3Runtime runtime, IM3ImportContext _ctx, uint64_t * _sp, void * _mem); 249 | 250 | M3Result m3_LinkRawFunction (IM3Module io_module, 251 | const char * const i_moduleName, 252 | const char * const i_functionName, 253 | const char * const i_signature, 254 | M3RawCall i_function); 255 | 256 | M3Result m3_LinkRawFunctionEx (IM3Module io_module, 257 | const char * const i_moduleName, 258 | const char * const i_functionName, 259 | const char * const i_signature, 260 | M3RawCall i_function, 261 | const void * i_userdata); 262 | 263 | const char* m3_GetModuleName (IM3Module i_module); 264 | void m3_SetModuleName (IM3Module i_module, const char* name); 265 | IM3Runtime m3_GetModuleRuntime (IM3Module i_module); 266 | 267 | //------------------------------------------------------------------------------------------------------------------------------- 268 | // globals 269 | //------------------------------------------------------------------------------------------------------------------------------- 270 | IM3Global m3_FindGlobal (IM3Module io_module, 271 | const char * const i_globalName); 272 | 273 | M3Result m3_GetGlobal (IM3Global i_global, 274 | IM3TaggedValue o_value); 275 | 276 | M3Result m3_SetGlobal (IM3Global i_global, 277 | const IM3TaggedValue i_value); 278 | 279 | M3ValueType m3_GetGlobalType (IM3Global i_global); 280 | 281 | //------------------------------------------------------------------------------------------------------------------------------- 282 | // functions 283 | //------------------------------------------------------------------------------------------------------------------------------- 284 | M3Result m3_Yield (void); 285 | 286 | // o_function is valid during the lifetime of the originating runtime 287 | M3Result m3_FindFunction (IM3Function * o_function, 288 | IM3Runtime i_runtime, 289 | const char * const i_functionName); 290 | 291 | uint32_t m3_GetArgCount (IM3Function i_function); 292 | uint32_t m3_GetRetCount (IM3Function i_function); 293 | M3ValueType m3_GetArgType (IM3Function i_function, uint32_t i_index); 294 | M3ValueType m3_GetRetType (IM3Function i_function, uint32_t i_index); 295 | 296 | M3Result m3_CallV (IM3Function i_function, ...); 297 | M3Result m3_CallVL (IM3Function i_function, va_list i_args); 298 | M3Result m3_Call (IM3Function i_function, uint32_t i_argc, const void * i_argptrs[]); 299 | M3Result m3_CallArgv (IM3Function i_function, uint32_t i_argc, const char * i_argv[]); 300 | 301 | M3Result m3_GetResultsV (IM3Function i_function, ...); 302 | M3Result m3_GetResultsVL (IM3Function i_function, va_list o_rets); 303 | M3Result m3_GetResults (IM3Function i_function, uint32_t i_retc, const void * o_retptrs[]); 304 | 305 | 306 | void m3_GetErrorInfo (IM3Runtime i_runtime, M3ErrorInfo* o_info); 307 | void m3_ResetErrorInfo (IM3Runtime i_runtime); 308 | 309 | const char* m3_GetFunctionName (IM3Function i_function); 310 | IM3Module m3_GetFunctionModule (IM3Function i_function); 311 | 312 | //------------------------------------------------------------------------------------------------------------------------------- 313 | // debug info 314 | //------------------------------------------------------------------------------------------------------------------------------- 315 | 316 | void m3_PrintRuntimeInfo (IM3Runtime i_runtime); 317 | void m3_PrintM3Info (void); 318 | void m3_PrintProfilerInfo (void); 319 | 320 | // The runtime owns the backtrace, do not free the backtrace you obtain. Returns NULL if there's no backtrace. 321 | IM3BacktraceInfo m3_GetBacktrace (IM3Runtime i_runtime); 322 | 323 | //------------------------------------------------------------------------------------------------------------------------------- 324 | // raw function definition helpers 325 | //------------------------------------------------------------------------------------------------------------------------------- 326 | 327 | # define m3ApiOffsetToPtr(offset) (void*)((uint8_t*)_mem + (uint32_t)(offset)) 328 | # define m3ApiPtrToOffset(ptr) (uint32_t)((uint8_t*)ptr - (uint8_t*)_mem) 329 | 330 | # define m3ApiReturnType(TYPE) TYPE* raw_return = ((TYPE*) (_sp++)); 331 | # define m3ApiGetArg(TYPE, NAME) TYPE NAME = * ((TYPE *) (_sp++)); 332 | # define m3ApiGetArgMem(TYPE, NAME) TYPE NAME = (TYPE)m3ApiOffsetToPtr(* ((uint32_t *) (_sp++))); 333 | 334 | # define m3ApiIsNullPtr(addr) ((void*)(addr) <= _mem) 335 | # define m3ApiCheckMem(addr, len) { if (M3_UNLIKELY(((void*)(addr) < _mem) || ((uint64_t)(uintptr_t)(addr) + (len)) > ((uint64_t)(uintptr_t)(_mem)+m3_GetMemorySize(runtime)))) m3ApiTrap(m3Err_trapOutOfBoundsMemoryAccess); } 336 | 337 | # define m3ApiRawFunction(NAME) const void * NAME (IM3Runtime runtime, IM3ImportContext _ctx, uint64_t * _sp, void * _mem) 338 | # define m3ApiReturn(VALUE) { *raw_return = (VALUE); return m3Err_none; } 339 | # define m3ApiTrap(VALUE) { return VALUE; } 340 | # define m3ApiSuccess() { return m3Err_none; } 341 | 342 | # if defined(M3_BIG_ENDIAN) 343 | # define m3ApiReadMem8(ptr) (* (uint8_t *)(ptr)) 344 | # define m3ApiReadMem16(ptr) m3_bswap16((* (uint16_t *)(ptr))) 345 | # define m3ApiReadMem32(ptr) m3_bswap32((* (uint32_t *)(ptr))) 346 | # define m3ApiReadMem64(ptr) m3_bswap64((* (uint64_t *)(ptr))) 347 | # define m3ApiWriteMem8(ptr, val) { * (uint8_t *)(ptr) = (val); } 348 | # define m3ApiWriteMem16(ptr, val) { * (uint16_t *)(ptr) = m3_bswap16((val)); } 349 | # define m3ApiWriteMem32(ptr, val) { * (uint32_t *)(ptr) = m3_bswap32((val)); } 350 | # define m3ApiWriteMem64(ptr, val) { * (uint64_t *)(ptr) = m3_bswap64((val)); } 351 | # else 352 | # define m3ApiReadMem8(ptr) (* (uint8_t *)(ptr)) 353 | # define m3ApiReadMem16(ptr) (* (uint16_t *)(ptr)) 354 | # define m3ApiReadMem32(ptr) (* (uint32_t *)(ptr)) 355 | # define m3ApiReadMem64(ptr) (* (uint64_t *)(ptr)) 356 | # define m3ApiWriteMem8(ptr, val) { * (uint8_t *)(ptr) = (val); } 357 | # define m3ApiWriteMem16(ptr, val) { * (uint16_t *)(ptr) = (val); } 358 | # define m3ApiWriteMem32(ptr, val) { * (uint32_t *)(ptr) = (val); } 359 | # define m3ApiWriteMem64(ptr, val) { * (uint64_t *)(ptr) = (val); } 360 | # endif 361 | 362 | #if defined(__cplusplus) 363 | } 364 | #endif 365 | 366 | #endif // wasm3_h 367 | -------------------------------------------------------------------------------- /wasm3/wasm3_defs.h: -------------------------------------------------------------------------------- 1 | // 2 | // wasm3_defs.h 3 | // 4 | // Created by Volodymyr Shymanskyy on 11/20/19. 5 | // Copyright © 2019 Volodymyr Shymanskyy. All rights reserved. 6 | // 7 | 8 | #ifndef wasm3_defs_h 9 | #define wasm3_defs_h 10 | 11 | #define M3_STR__(x) #x 12 | #define M3_STR(x) M3_STR__(x) 13 | 14 | #define M3_CONCAT__(a,b) a##b 15 | #define M3_CONCAT(a,b) M3_CONCAT__(a,b) 16 | 17 | /* 18 | * Detect compiler 19 | */ 20 | 21 | # if defined(__clang__) 22 | # define M3_COMPILER_CLANG 1 23 | # elif defined(__INTEL_COMPILER) 24 | # define M3_COMPILER_ICC 1 25 | # elif defined(__GNUC__) || defined(__GNUG__) 26 | # define M3_COMPILER_GCC 1 27 | # elif defined(_MSC_VER) 28 | # define M3_COMPILER_MSVC 1 29 | # else 30 | # warning "Compiler not detected" 31 | # endif 32 | 33 | # if defined(M3_COMPILER_CLANG) 34 | # if defined(WIN32) 35 | # define M3_COMPILER_VER __VERSION__ " for Windows" 36 | # else 37 | # define M3_COMPILER_VER __VERSION__ 38 | # endif 39 | # elif defined(M3_COMPILER_GCC) 40 | # define M3_COMPILER_VER "GCC " __VERSION__ 41 | # elif defined(M3_COMPILER_ICC) 42 | # define M3_COMPILER_VER __VERSION__ 43 | # elif defined(M3_COMPILER_MSVC) 44 | # define M3_COMPILER_VER "MSVC " M3_STR(_MSC_VER) 45 | # else 46 | # define M3_COMPILER_VER "unknown" 47 | # endif 48 | 49 | # ifdef __has_feature 50 | # define M3_COMPILER_HAS_FEATURE(x) __has_feature(x) 51 | # else 52 | # define M3_COMPILER_HAS_FEATURE(x) 0 53 | # endif 54 | 55 | # ifdef __has_builtin 56 | # define M3_COMPILER_HAS_BUILTIN(x) __has_builtin(x) 57 | # else 58 | # define M3_COMPILER_HAS_BUILTIN(x) 0 59 | # endif 60 | 61 | /* 62 | * Detect endianness 63 | */ 64 | 65 | # if defined(M3_COMPILER_MSVC) 66 | # define M3_LITTLE_ENDIAN 67 | # elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 68 | # define M3_LITTLE_ENDIAN 69 | # elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ 70 | # define M3_BIG_ENDIAN 71 | # else 72 | # error "Byte order not detected" 73 | # endif 74 | 75 | /* 76 | * Detect platform 77 | */ 78 | 79 | # if defined(M3_COMPILER_CLANG) || defined(M3_COMPILER_GCC) || defined(M3_COMPILER_ICC) 80 | # if defined(__wasm__) 81 | # define M3_ARCH "wasm" 82 | 83 | # elif defined(__x86_64__) 84 | # define M3_ARCH "x86_64" 85 | 86 | # elif defined(__i386__) 87 | # define M3_ARCH "i386" 88 | 89 | # elif defined(__aarch64__) 90 | # define M3_ARCH "arm64-v8a" 91 | 92 | # elif defined(__arm__) 93 | # if defined(__ARM_ARCH_7A__) 94 | # if defined(__ARM_NEON__) 95 | # if defined(__ARM_PCS_VFP) 96 | # define M3_ARCH "arm-v7a/NEON hard-float" 97 | # else 98 | # define M3_ARCH "arm-v7a/NEON" 99 | # endif 100 | # else 101 | # if defined(__ARM_PCS_VFP) 102 | # define M3_ARCH "arm-v7a hard-float" 103 | # else 104 | # define M3_ARCH "arm-v7a" 105 | # endif 106 | # endif 107 | # else 108 | # define M3_ARCH "arm" 109 | # endif 110 | 111 | # elif defined(__riscv) 112 | # if defined(__riscv_32e) 113 | # define _M3_ARCH_RV "rv32e" 114 | # elif __riscv_xlen == 128 115 | # define _M3_ARCH_RV "rv128i" 116 | # elif __riscv_xlen == 64 117 | # define _M3_ARCH_RV "rv64i" 118 | # elif __riscv_xlen == 32 119 | # define _M3_ARCH_RV "rv32i" 120 | # endif 121 | # if defined(__riscv_muldiv) 122 | # define _M3_ARCH_RV_M _M3_ARCH_RV "m" 123 | # else 124 | # define _M3_ARCH_RV_M _M3_ARCH_RV 125 | # endif 126 | # if defined(__riscv_atomic) 127 | # define _M3_ARCH_RV_A _M3_ARCH_RV_M "a" 128 | # else 129 | # define _M3_ARCH_RV_A _M3_ARCH_RV_M 130 | # endif 131 | # if defined(__riscv_flen) 132 | # define _M3_ARCH_RV_F _M3_ARCH_RV_A "f" 133 | # else 134 | # define _M3_ARCH_RV_F _M3_ARCH_RV_A 135 | # endif 136 | # if defined(__riscv_flen) && __riscv_flen >= 64 137 | # define _M3_ARCH_RV_D _M3_ARCH_RV_F "d" 138 | # else 139 | # define _M3_ARCH_RV_D _M3_ARCH_RV_F 140 | # endif 141 | # if defined(__riscv_compressed) 142 | # define _M3_ARCH_RV_C _M3_ARCH_RV_D "c" 143 | # else 144 | # define _M3_ARCH_RV_C _M3_ARCH_RV_D 145 | # endif 146 | # define M3_ARCH _M3_ARCH_RV_C 147 | 148 | # elif defined(__mips__) 149 | # if defined(__MIPSEB__) && defined(__mips64) 150 | # define M3_ARCH "mips64 " _MIPS_ARCH 151 | # elif defined(__MIPSEL__) && defined(__mips64) 152 | # define M3_ARCH "mips64el " _MIPS_ARCH 153 | # elif defined(__MIPSEB__) 154 | # define M3_ARCH "mips " _MIPS_ARCH 155 | # elif defined(__MIPSEL__) 156 | # define M3_ARCH "mipsel " _MIPS_ARCH 157 | # endif 158 | 159 | # elif defined(__PPC__) 160 | # if defined(__PPC64__) && defined(__LITTLE_ENDIAN__) 161 | # define M3_ARCH "ppc64le" 162 | # elif defined(__PPC64__) 163 | # define M3_ARCH "ppc64" 164 | # else 165 | # define M3_ARCH "ppc" 166 | # endif 167 | 168 | # elif defined(__sparc__) 169 | # if defined(__arch64__) 170 | # define M3_ARCH "sparc64" 171 | # else 172 | # define M3_ARCH "sparc" 173 | # endif 174 | 175 | # elif defined(__s390x__) 176 | # define M3_ARCH "s390x" 177 | 178 | # elif defined(__alpha__) 179 | # define M3_ARCH "alpha" 180 | 181 | # elif defined(__m68k__) 182 | # define M3_ARCH "m68k" 183 | 184 | # elif defined(__xtensa__) 185 | # define M3_ARCH "xtensa" 186 | 187 | # elif defined(__arc__) 188 | # define M3_ARCH "arc32" 189 | 190 | # elif defined(__AVR__) 191 | # define M3_ARCH "avr" 192 | # endif 193 | # endif 194 | 195 | # if defined(M3_COMPILER_MSVC) 196 | # if defined(_M_X64) 197 | # define M3_ARCH "x86_64" 198 | # elif defined(_M_IX86) 199 | # define M3_ARCH "i386" 200 | # elif defined(_M_ARM64) 201 | # define M3_ARCH "arm64" 202 | # elif defined(_M_ARM) 203 | # define M3_ARCH "arm" 204 | # endif 205 | # endif 206 | 207 | # if !defined(M3_ARCH) 208 | # warning "Architecture not detected" 209 | # define M3_ARCH "unknown" 210 | # endif 211 | 212 | /* 213 | * Byte swapping (for Big-Endian systems only) 214 | */ 215 | 216 | # if defined(M3_COMPILER_MSVC) 217 | # define m3_bswap16(x) _byteswap_ushort((x)) 218 | # define m3_bswap32(x) _byteswap_ulong((x)) 219 | # define m3_bswap64(x) _byteswap_uint64((x)) 220 | # elif defined(M3_COMPILER_GCC) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) 221 | // __builtin_bswap32/64 added in gcc 4.3, __builtin_bswap16 added in gcc 4.8 222 | # define m3_bswap16(x) __builtin_bswap16((x)) 223 | # define m3_bswap32(x) __builtin_bswap32((x)) 224 | # define m3_bswap64(x) __builtin_bswap64((x)) 225 | # elif defined(M3_COMPILER_CLANG) && M3_COMPILER_HAS_BUILTIN(__builtin_bswap16) 226 | # define m3_bswap16(x) __builtin_bswap16((x)) 227 | # define m3_bswap32(x) __builtin_bswap32((x)) 228 | # define m3_bswap64(x) __builtin_bswap64((x)) 229 | # elif defined(M3_COMPILER_ICC) 230 | # define m3_bswap16(x) __builtin_bswap16((x)) 231 | # define m3_bswap32(x) __builtin_bswap32((x)) 232 | # define m3_bswap64(x) __builtin_bswap64((x)) 233 | # else 234 | # ifdef __linux__ 235 | # include 236 | # else 237 | # include 238 | # endif 239 | # if defined(__bswap_16) 240 | # define m3_bswap16(x) __bswap_16((x)) 241 | # define m3_bswap32(x) __bswap_32((x)) 242 | # define m3_bswap64(x) __bswap_64((x)) 243 | # else 244 | # warning "Using naive (probably slow) bswap operations" 245 | static inline 246 | uint16_t m3_bswap16(uint16_t x) { 247 | return ((( x >> 8 ) & 0xffu ) | (( x & 0xffu ) << 8 )); 248 | } 249 | static inline 250 | uint32_t m3_bswap32(uint32_t x) { 251 | return ((( x & 0xff000000u ) >> 24 ) | 252 | (( x & 0x00ff0000u ) >> 8 ) | 253 | (( x & 0x0000ff00u ) << 8 ) | 254 | (( x & 0x000000ffu ) << 24 )); 255 | } 256 | static inline 257 | uint64_t m3_bswap64(uint64_t x) { 258 | return ((( x & 0xff00000000000000ull ) >> 56 ) | 259 | (( x & 0x00ff000000000000ull ) >> 40 ) | 260 | (( x & 0x0000ff0000000000ull ) >> 24 ) | 261 | (( x & 0x000000ff00000000ull ) >> 8 ) | 262 | (( x & 0x00000000ff000000ull ) << 8 ) | 263 | (( x & 0x0000000000ff0000ull ) << 24 ) | 264 | (( x & 0x000000000000ff00ull ) << 40 ) | 265 | (( x & 0x00000000000000ffull ) << 56 )); 266 | } 267 | # endif 268 | # endif 269 | 270 | /* 271 | * Other 272 | */ 273 | 274 | # if defined(M3_COMPILER_GCC) || defined(M3_COMPILER_CLANG) || defined(M3_COMPILER_ICC) 275 | # define M3_UNLIKELY(x) __builtin_expect(!!(x), 0) 276 | # define M3_LIKELY(x) __builtin_expect(!!(x), 1) 277 | # else 278 | # define M3_UNLIKELY(x) (x) 279 | # define M3_LIKELY(x) (x) 280 | # endif 281 | 282 | #endif // wasm3_defs_h 283 | --------------------------------------------------------------------------------