orca runtime initial commit
|
@ -0,0 +1,20 @@
|
|||
.DS_Store
|
||||
*.dSYM
|
||||
bin/*
|
||||
*.metallib
|
||||
*.pdb
|
||||
*.exe
|
||||
*.ilk
|
||||
*.vs
|
||||
*.obj
|
||||
*.lib
|
||||
*.dll
|
||||
*.sln
|
||||
*.wasm
|
||||
*.app
|
||||
*.dylib
|
||||
|
||||
Debug/*
|
||||
|
||||
src/bindgen_core_api.c
|
||||
src/bindgen_gles_api.c
|
|
@ -0,0 +1,3 @@
|
|||
[submodule "milepost"]
|
||||
path = milepost
|
||||
url = ssh://git@git.forkingpaths.dev/martinfouilleul/milepost.git
|
|
@ -0,0 +1,64 @@
|
|||
#!/bin/bash
|
||||
|
||||
target="$1"
|
||||
|
||||
if [ -z $target ] ; then
|
||||
target='orca'
|
||||
fi
|
||||
target=$(echo $target | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
if [ ! \( -e bin \) ] ; then
|
||||
mkdir ./bin
|
||||
fi
|
||||
|
||||
if [ ! \( -e resources \) ] ; then
|
||||
mkdir ./resources
|
||||
fi
|
||||
|
||||
|
||||
if [ $target = milepost ] ; then
|
||||
echo "building milepost"
|
||||
pushd milepost > /dev/null
|
||||
./build.sh lib "$2"
|
||||
popd > /dev/null
|
||||
|
||||
elif [ $target = wasm3 ] ; then
|
||||
|
||||
echo "building wasm3"
|
||||
mkdir ./bin/obj
|
||||
for file in ./ext/wasm3/source/*.c ; do
|
||||
name=$(basename $file)
|
||||
name=${name/.c/.o}
|
||||
clang -c -g -Wno-extern-initializer -Dd_m3VerboseErrorMessages -o ./bin/obj/$name -I./ext/wasm3/source $file
|
||||
done
|
||||
ar -rcs ./bin/libwasm3.a ./bin/obj/*.o
|
||||
rm -rf ./bin/obj
|
||||
|
||||
elif [ $target = orca ] ; then
|
||||
echo "building orca"
|
||||
|
||||
# copies libraries
|
||||
cp milepost/bin/mtl_renderer.metallib bin/
|
||||
cp milepost/bin/libmilepost.dylib bin/
|
||||
cp milepost/bin/libGLESv2.dylib bin/
|
||||
cp milepost/bin/libEGL.dylib bin/
|
||||
|
||||
INCLUDES="-Imilepost/src -Imilepost/src/util -Imilepost/src/platform -Iext/wasm3/source -Imilepost/ext/"
|
||||
LIBS="-Lbin -lmilepost -lwasm3"
|
||||
FLAGS="-g -DLOG_COMPILE_DEBUG -mmacos-version-min=10.15.4 -maes"
|
||||
|
||||
# generate wasm3 api bindings
|
||||
./scripts/bindgen.py core ./src
|
||||
./scripts/bindgen.py gles ./src
|
||||
|
||||
# compile orca
|
||||
clang $FLAGS $INCLUDES $LIBS -o bin/orca src/main.c
|
||||
|
||||
# fix libs imports
|
||||
install_name_tool -change "./bin/libmilepost.dylib" "@rpath/libmilepost.dylib" bin/orca
|
||||
install_name_tool -add_rpath "@executable_path/" bin/orca
|
||||
|
||||
|
||||
else
|
||||
echo "unknown build target $target"
|
||||
fi
|
|
@ -0,0 +1,5 @@
|
|||
[codespell]
|
||||
skip = ./test/wasi/brotli/alice29.txt,./test/.spec-*,./build*
|
||||
quiet-level = 2
|
||||
ignore-words-list = gameboy,iif,strng,woh
|
||||
|
|
@ -0,0 +1,180 @@
|
|||
name: publish
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*.*.*"
|
||||
|
||||
env:
|
||||
draft: true
|
||||
|
||||
jobs:
|
||||
wasm3-windows:
|
||||
runs-on: windows-latest
|
||||
name: ${{ matrix.config.target }}
|
||||
timeout-minutes: 10
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
config:
|
||||
- {target: wasm3-win-x64, platform: "-A x64", toolset: "-T ClangCL" }
|
||||
- {target: wasm3-win-x86, platform: "-A Win32", toolset: "-T ClangCL" }
|
||||
- {target: wasm3-strace-win-x64, platform: "-A x64", toolset: "-T ClangCL", cflags: "-DDEBUG -Dd_m3EnableStrace=2 -Dd_m3RecordBacktraces=1" }
|
||||
|
||||
env:
|
||||
LDFLAGS: -s
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Configure
|
||||
env:
|
||||
CFLAGS: ${{ matrix.config.cflags }}
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ${{ matrix.config.platform }} ${{ matrix.config.toolset }} ..
|
||||
- name: Build
|
||||
run: |
|
||||
cmake --build build --config Release
|
||||
cp ./build/Release/wasm3.exe ./${{ matrix.config.target }}.exe
|
||||
- name: Publish
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
draft: ${{ env.draft }}
|
||||
files: "*.exe"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
wasm3-linux-x64:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup musl
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install musl-tools
|
||||
- name: Setup cmake
|
||||
uses: jwlawson/actions-setup-cmake@v1.9
|
||||
with:
|
||||
cmake-version: '3.16.x'
|
||||
- name: Configure
|
||||
env:
|
||||
CC: musl-gcc
|
||||
LDFLAGS: "-static -s"
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake --version
|
||||
cmake ..
|
||||
- name: Build
|
||||
run: |
|
||||
cmake --build build --verbose
|
||||
cp ./build/wasm3 ./wasm3-linux-x64.elf
|
||||
- name: Publish
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
draft: ${{ env.draft }}
|
||||
files: "*.elf"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
wasm3-cosmopolitan:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Build αcτµαlly pδrταblε εxεcµταblε
|
||||
run: |
|
||||
cd platforms/cosmopolitan
|
||||
./build.sh
|
||||
cp ./wasm3.com ../../wasm3-cosmopolitan.com
|
||||
cp ./wasm3.com.dbg ../../wasm3-cosmopolitan.com.dbg
|
||||
- name: Publish
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
draft: ${{ env.draft }}
|
||||
files: |
|
||||
*.com
|
||||
*.com.dbg
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
wasm3-wasi:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
|
||||
env:
|
||||
LDFLAGS: -s
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Python dependencies
|
||||
run: |
|
||||
python3 -m pip install pip==20.1.1
|
||||
python3 -m pip install --upgrade setuptools wheel
|
||||
pip3 --version
|
||||
- name: Install Wasienv
|
||||
env:
|
||||
WASMER_RELEASE_TAG: "1.0.2"
|
||||
run: curl https://raw.githubusercontent.com/wasienv/wasienv/master/install.sh | sh
|
||||
- name: Configure
|
||||
run: |
|
||||
source $HOME/.wasienv/wasienv.sh
|
||||
mkdir build
|
||||
cd build
|
||||
wasimake cmake ..
|
||||
- name: Build
|
||||
run: |
|
||||
source $HOME/.wasienv/wasienv.sh
|
||||
cmake --build build
|
||||
cp ./build/wasm3.wasm ./wasm3-wasi.wasm
|
||||
- name: Configure, Build wasm3-strace
|
||||
env:
|
||||
CFLAGS: -DDEBUG -Dd_m3EnableStrace=2 -Dd_m3RecordBacktraces=1
|
||||
run: |
|
||||
source $HOME/.wasienv/wasienv.sh
|
||||
mkdir build-strace
|
||||
cd build-strace
|
||||
wasimake cmake ..
|
||||
cmake --build .
|
||||
cp ./wasm3.wasm ../wasm3-strace.wasm
|
||||
- name: Publish
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
draft: ${{ env.draft }}
|
||||
files: "*.wasm"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
wasm3-android-coremark:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: seanmiddleditch/gha-setup-ninja@master
|
||||
- name: Set up JDK 1.8
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.8
|
||||
- name: Install NDK
|
||||
run: |
|
||||
sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;22.1.7171670"
|
||||
sudo ${ANDROID_HOME}/tools/bin/sdkmanager --uninstall "cmake;3.18.1"
|
||||
sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "cmake;3.10.2.4988404"
|
||||
- name: Build
|
||||
run: |
|
||||
cd platforms/android
|
||||
./gradlew build
|
||||
- name: Copy
|
||||
run: |
|
||||
cp ./platforms/android/app/build/outputs/apk/debug/app-debug.apk ./wasm3-android-coremark.apk
|
||||
- name: Publish
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
draft: ${{ env.draft }}
|
||||
files: "*.apk"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@ -0,0 +1,619 @@
|
|||
name: tests
|
||||
|
||||
on:
|
||||
push:
|
||||
paths-ignore: ['**.md', '**.svg', '**.png']
|
||||
pull_request:
|
||||
paths-ignore: ['**.md', '**.svg', '**.png']
|
||||
|
||||
jobs:
|
||||
linux:
|
||||
runs-on: ubuntu-latest
|
||||
name: linux-${{ matrix.config.target }}
|
||||
timeout-minutes: 10
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
config:
|
||||
- {target: clang, cc: clang, }
|
||||
- {target: clang-x86, cc: clang, flags: -DCMAKE_C_FLAGS="-m32", install: "gcc-multilib" }
|
||||
- {target: gcc, cc: gcc, }
|
||||
# Builds without uvwasi
|
||||
- {target: gcc-no-uvwasi, cc: gcc, flags: -DBUILD_WASI=simple }
|
||||
- {target: clang-no-uvwasi, cc: clang, flags: -DBUILD_WASI=simple }
|
||||
# Debug builds
|
||||
- {target: gcc-debug, cc: gcc, flags: -DCMAKE_BUILD_TYPE=Debug }
|
||||
- {target: clang-no-uvwasi-debug, cc: clang, flags: -DCMAKE_BUILD_TYPE=Debug -DBUILD_WASI=simple }
|
||||
|
||||
# TODO: fails on numeric operations
|
||||
#- {target: gcc-x86, cc: gcc, flags: "-m32", install: "gcc-multilib" }
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install ${{ matrix.config.install }}
|
||||
if: ${{ matrix.config.install }}
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install ${{ matrix.config.install }}
|
||||
- name: Configure
|
||||
env:
|
||||
CC: ${{ matrix.config.cc }}
|
||||
CFLAGS: ${{ matrix.config.cflags }}
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ${{ matrix.config.flags }} ..
|
||||
- name: Build
|
||||
run: |
|
||||
cmake --build build
|
||||
- name: Test WebAssembly spec
|
||||
run: cd test && python3 run-spec-test.py
|
||||
- name: Test previous WebAssembly specs
|
||||
run: |
|
||||
cd test
|
||||
python3 run-spec-test.py --spec=v1.1
|
||||
- name: Test WASI apps
|
||||
run: cd test && python3 run-wasi-test.py
|
||||
|
||||
linux-alpine:
|
||||
runs-on: ubuntu-latest
|
||||
container: alpine:3.10
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Prepare
|
||||
run: apk add build-base cmake python3 git --update-cache
|
||||
- name: Configure
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
- name: Build
|
||||
run: cmake --build build
|
||||
- name: Test WebAssembly spec
|
||||
run: cd test && python3 run-spec-test.py
|
||||
- name: Test WASI apps
|
||||
run: cd test && python3 run-wasi-test.py
|
||||
|
||||
macos:
|
||||
runs-on: macos-latest
|
||||
name: macos-${{ matrix.config.target }}
|
||||
timeout-minutes: 10
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
config:
|
||||
- {target: uvwasi, }
|
||||
- {target: no-uvwasi, flags: -DBUILD_WASI=simple }
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Configure
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ${{ matrix.config.flags }} ..
|
||||
- name: Build
|
||||
run: |
|
||||
cmake --build build
|
||||
- name: Test WebAssembly spec
|
||||
run: cd test && python3 run-spec-test.py
|
||||
- name: Test previous WebAssembly specs
|
||||
run: |
|
||||
cd test
|
||||
python3 run-spec-test.py --spec=v1.1
|
||||
- name: Test WASI apps
|
||||
run: cd test && python3 run-wasi-test.py
|
||||
|
||||
windows:
|
||||
runs-on: windows-latest
|
||||
name: windows-${{ matrix.config.target }}
|
||||
timeout-minutes: 10
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
config:
|
||||
- {target: clang-x64, platform: "-A x64", toolset: "-T ClangCL" }
|
||||
- {target: msvc-x64, platform: "-A x64", toolset: "" }
|
||||
- {target: clang-x86, platform: "-A Win32", toolset: "-T ClangCL" }
|
||||
- {target: msvc-x86, platform: "-A Win32", toolset: "" }
|
||||
# Builds without uvwasi
|
||||
- {target: clang-x64-no-uvwasi, platform: "-A x64", toolset: "-T ClangCL", flags: "-DBUILD_WASI=simple" }
|
||||
- {target: msvc-x64-no-uvwasi, platform: "-A x64", toolset: "", flags: "-DBUILD_WASI=simple" }
|
||||
- {target: clang-x86-no-uvwasi, platform: "-A Win32", toolset: "-T ClangCL", flags: "-DBUILD_WASI=simple" }
|
||||
- {target: msvc-x86-no-uvwasi, platform: "-A Win32", toolset: "", flags: "-DBUILD_WASI=simple" }
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: cmd
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Configure
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ${{ matrix.config.platform }} ${{ matrix.config.toolset }} ${{ matrix.config.flags }} ..
|
||||
- name: Build
|
||||
run: |
|
||||
cmake --build build --config Release
|
||||
cp ./build/Release/wasm3.exe ./build/
|
||||
- name: Test WebAssembly spec
|
||||
run: |
|
||||
cd test
|
||||
python run-spec-test.py
|
||||
- name: Test previous WebAssembly specs
|
||||
run: |
|
||||
cd test
|
||||
python run-spec-test.py --spec=v1.1
|
||||
- name: Test WASI apps
|
||||
run: |
|
||||
cd test
|
||||
python run-wasi-test.py
|
||||
|
||||
cygwin-build:
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@main
|
||||
- name: Set up Cygwin
|
||||
uses: egor-tensin/setup-cygwin@master
|
||||
with:
|
||||
platform: x64
|
||||
packages: make gcc-g++ cmake
|
||||
- run: cd $GITHUB_WORKSPACE && cmake -DBUILD_WASI=simple . && make
|
||||
shell: C:\tools\cygwin\bin\bash.exe --login --norc -eo pipefail -o igncr '{0}'
|
||||
|
||||
wasi:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Python dependencies
|
||||
run: |
|
||||
python3 -m pip install pip==20.1.1
|
||||
python3 -m pip install --upgrade setuptools wheel
|
||||
pip3 --version
|
||||
- name: Install Wasienv
|
||||
env:
|
||||
WASMER_RELEASE_TAG: "1.0.2"
|
||||
run: curl https://raw.githubusercontent.com/wasienv/wasienv/master/install.sh | sh
|
||||
- name: Configure
|
||||
run: |
|
||||
source $HOME/.wasienv/wasienv.sh
|
||||
mkdir build-wasi
|
||||
cd build-wasi
|
||||
wasimake cmake ..
|
||||
- name: Build
|
||||
run: |
|
||||
source $HOME/.wasienv/wasienv.sh
|
||||
cmake --build build-wasi
|
||||
- name: Test WebAssembly spec (in Wasmer)
|
||||
run: |
|
||||
source $HOME/.wasmer/wasmer.sh
|
||||
cd test
|
||||
python3 run-spec-test.py --exec "wasmer run --mapdir=/:. ../build-wasi/wasm3.wasm -- --repl"
|
||||
|
||||
- name: Test WASI apps (in Wasmer)
|
||||
run: |
|
||||
source $HOME/.wasmer/wasmer.sh
|
||||
cd test
|
||||
python3 run-wasi-test.py --exec "wasmer run --mapdir=/:. ../build-wasi/wasm3.wasm --" --fast
|
||||
|
||||
- name: Configure (native)
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
- name: Build (native)
|
||||
run: |
|
||||
cmake --build build
|
||||
- name: Test WebAssembly spec (in Wasm3, self-hosting)
|
||||
run: |
|
||||
cd test
|
||||
cp ../build-wasi/wasm3.wasm ./
|
||||
python3 run-spec-test.py --exec "../build/wasm3 --stack-size 2097152 ../build-wasi/wasm3.wasm --repl"
|
||||
- name: Test WASI apps (in Wasm3, self-hosting)
|
||||
run: |
|
||||
cd test
|
||||
python3 run-wasi-test.py --fast --exec "../build/wasm3 --stack-size 2097152 ../build-wasi/wasm3.wasm"
|
||||
|
||||
ios:
|
||||
runs-on: macos-latest
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: List Xcode versions
|
||||
run: ls /Applications | grep Xcode
|
||||
- name: Select Xcode 12
|
||||
run: sudo xcode-select -switch /Applications/Xcode_12.4.app
|
||||
- name: Build (iPhone 11)
|
||||
run: |
|
||||
cd platforms/ios
|
||||
xcodebuild build -scheme wasm3 -project wasm3.xcodeproj -configuration Release -destination 'platform=iOS Simulator,name=iPhone 11,OS=14.4'
|
||||
|
||||
android:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: seanmiddleditch/gha-setup-ninja@master
|
||||
- name: Set up JDK 1.8
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.8
|
||||
- name: Install NDK
|
||||
run: |
|
||||
sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;22.1.7171670"
|
||||
sudo ${ANDROID_HOME}/tools/bin/sdkmanager --uninstall "cmake;3.18.1"
|
||||
sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "cmake;3.10.2.4988404"
|
||||
- name: Build
|
||||
run: |
|
||||
cd platforms/android
|
||||
./gradlew build
|
||||
|
||||
python:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
path: wasm3
|
||||
- name: Checkout pywasm3
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: wasm3/pywasm3
|
||||
path: pywasm3
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
- name: Update and Build Python module
|
||||
run: |
|
||||
rm -rf ./pywasm3/wasm3
|
||||
cp -r wasm3/source ./pywasm3/wasm3
|
||||
pip install ./pywasm3
|
||||
- name: Install WABT
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install wabt
|
||||
- name: Test
|
||||
run: |
|
||||
pip install pytest
|
||||
cd ./pywasm3
|
||||
pytest
|
||||
|
||||
cosmopolitan:
|
||||
runs-on: ubuntu-20.04
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Build αcτµαlly pδrταblε εxεcµταblε
|
||||
run: |
|
||||
cd platforms/cosmopolitan
|
||||
gcc -v
|
||||
./build.sh
|
||||
- name: Prepare tests
|
||||
run: |
|
||||
cd test
|
||||
cp ../platforms/cosmopolitan/wasm3.com ./wasm3-lin.com
|
||||
cp ../platforms/cosmopolitan/wasm3.com ./wasm3-win.com
|
||||
sudo sh -c "echo ':APE:M::MZqFpD::/bin/sh:' >/proc/sys/fs/binfmt_misc/register"
|
||||
- name: Test WebAssembly spec
|
||||
run: |
|
||||
cd test
|
||||
python3 run-spec-test.py --exec "./wasm3-lin.com --repl"
|
||||
- name: Test WASI apps
|
||||
run: |
|
||||
cd test
|
||||
python3 run-wasi-test.py --fast --exec "./wasm3-lin.com"
|
||||
- name: Install Wine64
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install wine64
|
||||
wine --version
|
||||
- name: Test WebAssembly spec (in Wine)
|
||||
run: |
|
||||
cd test
|
||||
python3 run-spec-test.py --exec "wine ./wasm3-win.com --repl"
|
||||
- name: Test WASI apps (in Wine)
|
||||
run: |
|
||||
cd test
|
||||
python3 run-wasi-test.py --fast --exec "wine ./wasm3-win.com"
|
||||
|
||||
cross-qemu:
|
||||
runs-on: ubuntu-20.04
|
||||
name: cross-qemu-${{ matrix.config.target }}
|
||||
timeout-minutes: 10
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
config:
|
||||
#- {target: i386, toolchain: gcc-multilib, cc: clang -m32, qemu: qemu-i386-static }
|
||||
- {target: arm, toolchain: gcc-arm-linux-gnueabi, cc: arm-linux-gnueabi-gcc, qemu: qemu-arm-static }
|
||||
- {target: armhf, toolchain: gcc-arm-linux-gnueabihf, cc: arm-linux-gnueabihf-gcc, qemu: qemu-arm-static }
|
||||
- {target: aarch64, toolchain: gcc-aarch64-linux-gnu, cc: aarch64-linux-gnu-gcc, qemu: qemu-aarch64-static }
|
||||
- {target: riscv64, toolchain: gcc-riscv64-linux-gnu, cc: riscv64-linux-gnu-gcc, qemu: qemu-riscv64-static }
|
||||
- {target: ppc, toolchain: gcc-powerpc-linux-gnu, cc: powerpc-linux-gnu-gcc, qemu: qemu-ppc-static }
|
||||
- {target: ppc64, toolchain: gcc-powerpc64-linux-gnu, cc: powerpc64-linux-gnu-gcc, qemu: qemu-ppc64-static }
|
||||
#- {target: ppc64le, toolchain: gcc-powerpc64le-linux-gnu, cc: powerpc64le-linux-gnu-gcc, qemu: qemu-ppc64le-static }
|
||||
- {target: s390x, toolchain: gcc-s390x-linux-gnu, cc: s390x-linux-gnu-gcc, qemu: qemu-s390x-static }
|
||||
- {target: mips, toolchain: gcc-mips-linux-gnu, cc: mips-linux-gnu-gcc, qemu: qemu-mips-static }
|
||||
- {target: mips64, toolchain: gcc-mips64-linux-gnuabi64, cc: mips64-linux-gnuabi64-gcc, qemu: qemu-mips64-static }
|
||||
- {target: mipsel, toolchain: gcc-mipsel-linux-gnu, cc: mipsel-linux-gnu-gcc, qemu: qemu-mipsel-static }
|
||||
- {target: mips64el,toolchain: gcc-mips64el-linux-gnuabi64, cc: mips64el-linux-gnuabi64-gcc,qemu: qemu-mips64el-static }
|
||||
- {target: alpha, toolchain: gcc-alpha-linux-gnu, cc: alpha-linux-gnu-gcc, qemu: qemu-alpha-static }
|
||||
- {target: sparc64, toolchain: gcc-sparc64-linux-gnu, cc: sparc64-linux-gnu-gcc, qemu: qemu-sparc64-static, skip_wasi: true }
|
||||
|
||||
#- {target: i386 (u64 slots), toolchain: gcc-multilib, cc: clang -m32, qemu: qemu-i386-static, cflags: -Dd_m3Use32BitSlots=0 }
|
||||
- {target: arm (u64 slots), toolchain: gcc-arm-linux-gnueabi, cc: arm-linux-gnueabi-gcc, qemu: qemu-arm-static, cflags: -Dd_m3Use32BitSlots=0 }
|
||||
- {target: aarch64 (u64 slots), toolchain: gcc-aarch64-linux-gnu, cc: aarch64-linux-gnu-gcc, qemu: qemu-aarch64-static, cflags: -Dd_m3Use32BitSlots=0 }
|
||||
- {target: ppc (u64 slots), toolchain: gcc-powerpc-linux-gnu, cc: powerpc-linux-gnu-gcc, qemu: qemu-ppc-static, cflags: -Dd_m3Use32BitSlots=0 }
|
||||
- {target: ppc64 (u64 slots), toolchain: gcc-powerpc64-linux-gnu, cc: powerpc64-linux-gnu-gcc, qemu: qemu-ppc64-static, cflags: -Dd_m3Use32BitSlots=0 }
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install QEMU
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install qemu-user-static
|
||||
- name: Install ${{ matrix.config.toolchain }}
|
||||
run: |
|
||||
sudo apt install ${{ matrix.config.toolchain }}
|
||||
- name: Build
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
${{ matrix.config.cc }} -DASSERTS -Dd_m3HasWASI ${{ matrix.config.cflags }} \
|
||||
-I../source ../source/*.c ../platforms/app/main.c \
|
||||
-O3 -g0 -flto -lm -static \
|
||||
-o wasm3
|
||||
- name: Test WebAssembly spec
|
||||
run: |
|
||||
cd test
|
||||
python3 run-spec-test.py --exec "${{ matrix.config.qemu }} ../build/wasm3 --repl"
|
||||
- name: Test WASI apps
|
||||
if: ${{ !matrix.config.skip_wasi }}
|
||||
run: |
|
||||
cd test
|
||||
python3 run-wasi-test.py --fast --exec "${{ matrix.config.qemu }} ../build/wasm3"
|
||||
|
||||
platformio:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Install PlatformIO
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -U platformio
|
||||
- name: Build AVR ATmega1284
|
||||
run: |
|
||||
cd platforms/embedded/arduino
|
||||
pio run -e mega1284
|
||||
! nm .pio/build/mega1284/firmware.elf | grep printf
|
||||
- name: Build ESP8266
|
||||
run: |
|
||||
cd platforms/embedded/esp8266
|
||||
pio run
|
||||
# TODO:
|
||||
#- name: Build ESP32
|
||||
# run: |
|
||||
# cd platforms/embedded/esp32-pio
|
||||
# pio run
|
||||
|
||||
platformio-arm:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Install PlatformIO
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -U platformio
|
||||
- name: Build Arduino MKR1000
|
||||
run: |
|
||||
cd platforms/embedded/arduino
|
||||
pio run -e mkr1000
|
||||
- name: Build Blue Pill (JeeH)
|
||||
run: |
|
||||
cd platforms/embedded/bluepill
|
||||
pio run
|
||||
- name: Build TinyBLE
|
||||
run: |
|
||||
cd platforms/embedded/arduino
|
||||
pio run -e tinyBLE
|
||||
- name: Build MXChip AZ3166
|
||||
run: |
|
||||
cd platforms/embedded/arduino
|
||||
pio run -e az3166
|
||||
|
||||
platformio-riscv:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Install PlatformIO
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -U platformio
|
||||
- name: Build HiFive1
|
||||
run: |
|
||||
cd platforms/embedded/hifive1
|
||||
pio run
|
||||
- name: Build Sipeed MAIX
|
||||
run: |
|
||||
cd platforms/embedded/arduino
|
||||
pio run -e maix
|
||||
|
||||
particle:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
if: "github.event_name == 'push'"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Particle CLI
|
||||
run: sudo npm install -g particle-cli
|
||||
- name: Log in
|
||||
env:
|
||||
PARTICLE_TOKEN: ${{ secrets.PARTICLE_TOKEN }}
|
||||
run: particle login --token $PARTICLE_TOKEN
|
||||
- name: Build Photon
|
||||
run: |
|
||||
cd platforms/embedded/particle
|
||||
particle compile --followSymlinks photon
|
||||
particle compile --followSymlinks argon
|
||||
|
||||
esp32-idf:
|
||||
runs-on: ubuntu-latest
|
||||
container: igrr/idf-qemu:release-v4.0-esp-develop-20191228
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Build for ESP32 (IDF v4.0)
|
||||
run: |
|
||||
. $IDF_PATH/export.sh
|
||||
cd platforms/embedded/esp32-idf
|
||||
export EXTRA_CFLAGS="-Werror"
|
||||
idf.py build
|
||||
shell: bash
|
||||
- name: Test for ESP32 in QEMU
|
||||
run: |
|
||||
cd platforms/embedded/esp32-idf
|
||||
make-flash-img.sh wasm3 flash_img.bin
|
||||
qemu-system-xtensa -machine esp32 -nographic -drive file=flash_img.bin,if=mtd,format=raw -no-reboot | tee out.txt
|
||||
grep "Result: 46368" out.txt
|
||||
grep "Elapsed: " out.txt
|
||||
grep "Restarting..." out.txt
|
||||
test $(($(grep "ets Jun 8 2016" out.txt | wc -l))) -eq 1
|
||||
- name: Check that IDF and PIO examples are in sync
|
||||
run: |
|
||||
diff -q platforms/embedded/esp32-idf/main/main.cpp platforms/embedded/esp32-pio/src/main.cpp
|
||||
# TODO: also check that the build flags are in sync
|
||||
|
||||
cpp:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Configure
|
||||
run: |
|
||||
cd platforms/cpp
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
- name: Build
|
||||
run: |
|
||||
cd platforms/cpp
|
||||
cmake --build build
|
||||
- name: Run
|
||||
run: |
|
||||
cd platforms/cpp/build
|
||||
./wasm3_cpp_example
|
||||
|
||||
as-cpp:
|
||||
name: maintenance (build as C++)
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Build
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
clang -xc++ -Dd_m3HasWASI \
|
||||
-I../source ../source/*.c ../platforms/app/main.c \
|
||||
-O3 -g0 -lm \
|
||||
-o wasm3
|
||||
- name: Test
|
||||
run: ./build/wasm3 ./test/wasi/simple/test.wasm
|
||||
|
||||
with-logs:
|
||||
name: maintenance (debug logs)
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Build
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
clang -xc++ -Dd_m3HasWASI -DDEBUG \
|
||||
-Dd_m3EnableOpTracing=1 \
|
||||
-Dd_m3EnableStrace=1 \
|
||||
-Dd_m3LogParse=1 \
|
||||
-Dd_m3LogModule=1 \
|
||||
-Dd_m3LogCompile=1 \
|
||||
-Dd_m3LogWasmStack=1 \
|
||||
-Dd_m3LogEmit=1 \
|
||||
-Dd_m3LogCodePages=1 \
|
||||
-Dd_m3LogRuntime=1 \
|
||||
-Dd_m3LogNativeStack=1 \
|
||||
-I../source ../source/*.c ../platforms/app/main.c \
|
||||
-O3 -g0 -lm \
|
||||
-o wasm3
|
||||
- name: Test
|
||||
run: ./build/wasm3 ./test/wasi/simple/test.wasm > /dev/null
|
||||
|
||||
preprocessed-ops:
|
||||
name: maintenance (preprocess ops)
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install sponge
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install moreutils
|
||||
- name: Build
|
||||
run: |
|
||||
make -f extra/utils.mk preprocess
|
||||
mkdir build
|
||||
cd build
|
||||
gcc -Dd_m3HasWASI \
|
||||
-I../source ../source/*.c ../platforms/app/main.c \
|
||||
-O3 -g0 -lm \
|
||||
-o wasm3
|
||||
- name: Test
|
||||
run: ./build/wasm3 ./test/wasi/simple/test.wasm
|
||||
|
||||
spellcheck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
- name: Install codespell
|
||||
run: |
|
||||
pip install codespell
|
||||
- name: Spellcheck
|
||||
run: |
|
||||
codespell
|
|
@ -0,0 +1,13 @@
|
|||
/build*
|
||||
/source-*
|
||||
/.toolchains
|
||||
test/.spec-*
|
||||
test/*.log
|
||||
test/tailcall/*.S
|
||||
node_modules/
|
||||
__pycache__/
|
||||
.project
|
||||
.cproject
|
||||
.settings
|
||||
.vscode
|
||||
.DS_Store
|
|
@ -0,0 +1,225 @@
|
|||
cmake_minimum_required(VERSION 3.11)
|
||||
|
||||
# Detect WasiEnv
|
||||
if(DEFINED ENV{WASI_CC})
|
||||
set(WASIENV 1)
|
||||
endif()
|
||||
|
||||
# Detect MinGW
|
||||
if(WIN32 AND CMAKE_C_COMPILER_ID MATCHES "GNU")
|
||||
set(MINGW 1)
|
||||
endif()
|
||||
|
||||
# Set options
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "set build type to Release")
|
||||
endif()
|
||||
|
||||
if(WASIENV)
|
||||
set(BUILD_WASI "metawasi" CACHE STRING "WASI implementation")
|
||||
elseif(EMSCRIPTEN OR EMSCRIPTEN_LIB)
|
||||
set(BUILD_WASI "none" CACHE STRING "WASI implementation")
|
||||
else()
|
||||
set(BUILD_WASI "uvwasi" CACHE STRING "WASI implementation")
|
||||
endif()
|
||||
set_property(CACHE BUILD_WASI PROPERTY STRINGS none simple uvwasi metawasi)
|
||||
|
||||
option(BUILD_NATIVE "Build with machine-specific optimisations" ON)
|
||||
|
||||
set(OUT_FILE "wasm3")
|
||||
|
||||
if(NOT APP_DIR)
|
||||
set(APP_DIR "platforms/app")
|
||||
endif()
|
||||
|
||||
# Configure the toolchain
|
||||
|
||||
if(CLANG OR CLANG_SUFFIX)
|
||||
set(CMAKE_C_COMPILER "clang${CLANG_SUFFIX}")
|
||||
set(CMAKE_CXX_COMPILER "clang++${CLANG_SUFFIX}")
|
||||
|
||||
if(BUILD_FUZZ)
|
||||
set(CMAKE_TRY_COMPILE_TARGET_TYPE "STATIC_LIBRARY")
|
||||
set(OUT_FILE "wasm3-fuzzer")
|
||||
set(APP_DIR "platforms/app_fuzz")
|
||||
set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "set build type to Debug")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=fuzzer,address")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=fuzzer,address")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CLANG_CL)
|
||||
set(CMAKE_C_COMPILER "clang-cl")
|
||||
set(CMAKE_CXX_COMPILER "clang-cl")
|
||||
set(CMAKE_LINKER "lld-link")
|
||||
endif()
|
||||
|
||||
if(EMSCRIPTEN OR EMSCRIPTEN_LIB)
|
||||
set(CMAKE_C_COMPILER "emcc")
|
||||
set(CMAKE_CXX_COMPILER "em++")
|
||||
|
||||
if (EMSCRIPTEN_LIB)
|
||||
set(APP_DIR "platforms/emscripten_lib")
|
||||
set(OUT_FILE "wasm3.wasm")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -s STANDALONE_WASM")
|
||||
else()
|
||||
set(APP_DIR "platforms/emscripten")
|
||||
set(OUT_FILE "wasm3.html")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(BUILD_32BIT)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32")
|
||||
endif()
|
||||
|
||||
|
||||
|
||||
project(wasm3)
|
||||
|
||||
message("----")
|
||||
message("Generator: ${CMAKE_GENERATOR}")
|
||||
message("Compiler: ${CMAKE_C_COMPILER_ID}")
|
||||
message("Build Type: ${CMAKE_BUILD_TYPE}")
|
||||
|
||||
|
||||
include(CheckIPOSupported)
|
||||
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
set(CMAKE_C_STANDARD_REQUIRED YES)
|
||||
set(CMAKE_C_EXTENSIONS NO)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED YES)
|
||||
set(CMAKE_CXX_EXTENSIONS NO)
|
||||
|
||||
|
||||
file(GLOB app_srcs "${APP_DIR}/*.c")
|
||||
add_executable(${OUT_FILE} ${app_srcs})
|
||||
|
||||
#-fno-optimize-sibling-calls
|
||||
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DDEBUG=1")
|
||||
|
||||
if(EMSCRIPTEN OR EMSCRIPTEN_LIB)
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -s GLOBAL_BASE=1024 -s TOTAL_STACK=2MB -s INITIAL_MEMORY=4MB -s ALLOW_MEMORY_GROWTH")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -s EXPORTED_FUNCTIONS=\"[\\\"_malloc\\\",\\\"_free\\\",\\\"_main\\\"]\"")
|
||||
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O3 -flto -Wfatal-errors -s ASSERTIONS=0")
|
||||
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} --strip-all --gc-sections")
|
||||
|
||||
if(WASM_EXT)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mbulk-memory -mnontrapping-fptoint -msign-ext -mtail-call")
|
||||
endif()
|
||||
|
||||
elseif(WASIENV)
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Dd_m3HasTracer")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O3 -Wfatal-errors -fomit-frame-pointer -fno-stack-check -fno-stack-protector")
|
||||
|
||||
if(WASM_EXT)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mbulk-memory -mnontrapping-fptoint -msign-ext -mtail-call")
|
||||
endif()
|
||||
|
||||
# TODO: LTO breaks wasm imports currently:
|
||||
# https://www.mail-archive.com/llvm-bugs@lists.llvm.org/msg36273.html
|
||||
|
||||
#-flto -Wl,--lto-O3
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,stack-size=8388608")
|
||||
|
||||
elseif(WIN32 AND NOT MINGW)
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Dd_m3HasTracer -D_CRT_SECURE_NO_WARNINGS /WX- /diagnostics:column")
|
||||
|
||||
string(REGEX REPLACE "/W[0-4]" "/W0" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
|
||||
|
||||
if (CMAKE_C_COMPILER_ID MATCHES "MSVC")
|
||||
|
||||
if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /d2noftol3")
|
||||
endif()
|
||||
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /Oxs /Oy /GS- /Zi /Zo /arch:AVX2")
|
||||
|
||||
# Uncomment this if you want to disassemble the release build,
|
||||
# for example: dumpbin /DISASM wasm3.exe /out:wasm3.S
|
||||
#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DEBUG:FULL")
|
||||
|
||||
else()
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /Oxs /Oy /GS- /Qvec -Clang -O3")
|
||||
endif()
|
||||
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /STACK:8388608") # stack size
|
||||
|
||||
else()
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Dd_m3HasTracer") #-Dd_m3FixedHeap=1048576
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wparentheses -Wundef -Wpointer-arith -Wstrict-aliasing=2")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror=implicit-function-declaration") # -Werror=cast-align
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-function -Wno-unused-variable -Wno-unused-parameter -Wno-missing-field-initializers")
|
||||
if (CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||
# TODO: Place clang-specific options here
|
||||
elseif(CMAKE_C_COMPILER_ID MATCHES "Intel")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fp-model precise")
|
||||
elseif(CMAKE_C_COMPILER_ID MATCHES "GNU")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wjump-misses-init")
|
||||
endif()
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0 -save-temps")
|
||||
|
||||
set(CMAKE_C_FLAGS_RELEASE "-O3 -Wfatal-errors -fomit-frame-pointer -fno-stack-check -fno-stack-protector") #-fno-inline
|
||||
|
||||
if(BUILD_NATIVE)
|
||||
if(APPLE AND CMAKE_C_COMPILER_ID MATCHES "Clang" AND CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "arm64")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -mcpu=native")
|
||||
else()
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -march=native")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "-O0")
|
||||
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "-O3")
|
||||
|
||||
target_link_libraries(${OUT_FILE} m)
|
||||
|
||||
endif()
|
||||
|
||||
target_link_libraries(${OUT_FILE} m3)
|
||||
|
||||
if(BUILD_WASI MATCHES "simple")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Dd_m3HasWASI")
|
||||
elseif(BUILD_WASI MATCHES "metawasi")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Dd_m3HasMetaWASI")
|
||||
elseif(BUILD_WASI MATCHES "uvwasi")
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
uvwasi
|
||||
GIT_REPOSITORY https://github.com/nodejs/uvwasi.git
|
||||
GIT_TAG b599542f7ce001e04cdff9db82b05fee96bb3332
|
||||
)
|
||||
|
||||
FetchContent_GetProperties(uvwasi)
|
||||
if(NOT uvwasi_POPULATED)
|
||||
FetchContent_Populate(uvwasi)
|
||||
include_directories("${uvwasi_SOURCE_DIR}/include")
|
||||
add_subdirectory(${uvwasi_SOURCE_DIR} ${uvwasi_BINARY_DIR} EXCLUDE_FROM_ALL)
|
||||
endif()
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Dd_m3HasUVWASI")
|
||||
target_link_libraries(${OUT_FILE} uvwasi_a uv_a)
|
||||
endif()
|
||||
|
||||
check_ipo_supported(RESULT result)
|
||||
if(result AND NOT WASIENV) # TODO: LTO breaks wasm imports
|
||||
set_property(TARGET ${OUT_FILE} PROPERTY INTERPROCEDURAL_OPTIMIZATION True)
|
||||
message("LTO: ON")
|
||||
else()
|
||||
message("LTO: OFF")
|
||||
endif()
|
||||
|
||||
add_subdirectory(source)
|
||||
|
||||
message("Flags: ${CMAKE_C_FLAGS}")
|
||||
message("Debug flags: ${CMAKE_C_FLAGS_DEBUG}")
|
||||
message("Release flags: ${CMAKE_C_FLAGS_RELEASE}")
|
||||
|
||||
message("----")
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2019 Steven Massey, Volodymyr Shymanskyy
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,118 @@
|
|||
[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner-direct.svg)](https://vshymanskyy.github.io/StandWithUkraine)
|
||||
|
||||
<img align="right" width="30%" src="/extra/screenshot-ios.png">
|
||||
|
||||
# <img src="/extra/wasm-symbol.svg" width="32" height="32" /> Wasm3
|
||||
|
||||
[![WAPM](https://wapm.io/package/vshymanskyy/wasm3/badge.svg)](https://wapm.io/package/vshymanskyy/wasm3)
|
||||
[![GitHub issues](https://img.shields.io/github/issues-raw/wasm3/wasm3?style=flat-square&label=issues&color=success)](https://github.com/wasm3/wasm3/issues)
|
||||
[![Tests status](https://img.shields.io/github/workflow/status/wasm3/wasm3/tests/main?style=flat-square&logo=github&label=tests)](https://github.com/wasm3/wasm3/actions)
|
||||
[![Fuzzing Status](https://img.shields.io/badge/oss--fuzz-fuzzing-success?style=flat-square)](https://bugs.chromium.org/p/oss-fuzz/issues/list?can=1&q=proj:wasm3)
|
||||
[![GitHub license](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](https://github.com/wasm3/wasm3)
|
||||
|
||||
The fastest WebAssembly interpreter, and the most universal runtime.
|
||||
<sub>Based on [**CoreMark 1.0**](./docs/Performance.md) and [**independent**](https://00f.net/2021/02/22/webassembly-runtimes-benchmarks) benchmarks. Your mileage may vary.</sub>
|
||||
|
||||
|
||||
[![Twitter](https://img.shields.io/twitter/follow/wasm3_engine?style=flat-square&color=1da1f2&label=twitter&logo=twitter)](https://twitter.com/wasm3_engine)
|
||||
[![Discord](https://img.shields.io/discord/671415645073702925?style=flat-square&logo=discord&color=7289da&label=discord)](https://discord.gg/qmZjgnd)
|
||||
[![Telegram](https://img.shields.io/badge/telegram-chat-0088cc?style=flat-square&logo=telegram)](https://t.me/joinchat/DD8s3xVG8Vj_LxRDm52eTQ)
|
||||
|
||||
## Getting Started
|
||||
|
||||
Here's a small [getting started guide](https://wapm.io/package/vshymanskyy/wasm3). Click here to start:
|
||||
|
||||
[![LIVE DEMO](extra/button.png)](https://webassembly.sh/?run-command=wasm3)
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
**Please follow the [installation instructions](./docs/Installation.md).**
|
||||
|
||||
Wasm3 can also be used as a library for:
|
||||
|
||||
[<img src="https://cdn.rawgit.com/simple-icons/simple-icons/develop/icons/python.svg" width="18" height="18" /> Python3](https://github.com/wasm3/pywasm3) │
|
||||
[<img src="https://cdn.rawgit.com/simple-icons/simple-icons/develop/icons/rust.svg" width="18" height="18" /> Rust](https://github.com/Veykril/wasm3-rs) │
|
||||
[<img src="https://cdn.rawgit.com/simple-icons/simple-icons/develop/icons/cplusplus.svg" width="18" height="18" /> C/C++](https://github.com/wasm3/wasm3) │
|
||||
[<img src="https://cdn.rawgit.com/simple-icons/simple-icons/develop/icons/go.svg" width="18" height="18" /> GoLang](https://github.com/matiasinsaurralde/go-wasm3) │
|
||||
[<img src="https://cdn.rawgit.com/simple-icons/simple-icons/develop/icons/zig.svg" width="18" height="18" /> Zig](https://github.com/alichay/zig-wasm3) │
|
||||
[<img src="https://cdn.jsdelivr.net/gh/simple-icons/simple-icons@develop/icons/perl.svg" width="18" height="18" /> Perl](https://metacpan.org/pod/Wasm::Wasm3)
|
||||
[<img src="https://cdn.rawgit.com/simple-icons/simple-icons/develop/icons/swift.svg" width="18" height="18" /> Swift](https://github.com/shareup/wasm-interpreter-apple) │
|
||||
[<img src="https://cdn.rawgit.com/simple-icons/simple-icons/develop/icons/dotnet.svg" width="18" height="18" /> .Net](https://github.com/tana/Wasm3DotNet) │
|
||||
[<img src="https://cdn.rawgit.com/simple-icons/simple-icons/develop/icons/arduino.svg" width="18" height="18" /> Arduino, PlatformIO, Particle](https://github.com/wasm3/wasm3-arduino) │ [QuickJS](https://github.com/saghul/txiki.js)
|
||||
|
||||
## Status
|
||||
|
||||
`wasm3` passes the [WebAssembly spec testsuite](https://github.com/WebAssembly/spec/tree/master/test/core) and is able to run many `WASI` apps.
|
||||
|
||||
Minimum useful system requirements: **~64Kb** for code and **~10Kb** RAM
|
||||
|
||||
`wasm3` runs on a wide range of architectures (`x86`, `x86_64`, `ARM`, `RISC-V`, `PowerPC`, `MIPS`, `Xtensa`, `ARC32`, ...) and [platforms](/platforms):
|
||||
- <img src="https://cdn.rawgit.com/simple-icons/simple-icons/develop/icons/linux.svg" width="18" height="18" /> Linux,
|
||||
<img src="https://cdn.rawgit.com/simple-icons/simple-icons/develop/icons/windows.svg" width="18" height="18" /> Windows,
|
||||
<img src="https://cdn.rawgit.com/simple-icons/simple-icons/develop/icons/apple.svg" width="18" height="18" /> OS X,
|
||||
<img src="https://cdn.rawgit.com/simple-icons/simple-icons/develop/icons/freebsd.svg" width="18" height="18" /> FreeBSD,
|
||||
<img src="https://cdn.rawgit.com/simple-icons/simple-icons/develop/icons/android.svg" width="18" height="18" /> Android,
|
||||
<img src="https://cdn.rawgit.com/simple-icons/simple-icons/develop/icons/apple.svg" width="18" height="18" /> iOS
|
||||
- <img src="https://cdn.rawgit.com/feathericons/feather/master/icons/wifi.svg" width="18" height="18" /> OpenWrt, Yocto, Buildroot (routers, modems, etc.)
|
||||
- <img src="https://cdn.rawgit.com/simple-icons/simple-icons/develop/icons/raspberrypi.svg" width="18" height="18" /> Raspberry Pi, Orange Pi and other SBCs
|
||||
- <img src="https://cdn.rawgit.com/feathericons/feather/master/icons/cpu.svg" width="18" height="18" /> MCUs: Arduino, ESP8266, ESP32, Particle, ... [see full list](./docs/Hardware.md)
|
||||
- <img src="https://cdn.rawgit.com/simple-icons/simple-icons/develop/icons/firefoxbrowser.svg" width="18" height="18" /> Browsers. Yes, using WebAssembly itself!
|
||||
- <img src="extra/wasm-symbol.svg" width="18" height="18" /> `wasm3` can execute `wasm3` (self-hosting)
|
||||
|
||||
## Features
|
||||
|
||||
| Webassembly [Core Proposals][WasmProps] | Extra |
|
||||
| --- | --- |
|
||||
| ☑ Import/Export of Mutable Globals | ☑ Structured execution tracing |
|
||||
| ☑ Non-trapping float-to-int conversions | ☑ Big-Endian systems support |
|
||||
| ☑ Sign-extension operators | ☑ Wasm and WASI self-hosting |
|
||||
| ☑ Multi-value | ☑ Gas metering |
|
||||
| ☑ Bulk memory operations (partial support) | ☑ Linear memory limit (< 64KiB) |
|
||||
| ☐ Multiple memories |
|
||||
| ☐ Reference types |
|
||||
| ☐ Tail call optimization |
|
||||
| ☐ Fixed-width SIMD |
|
||||
| ☐ Exception handling |
|
||||
|
||||
## Motivation
|
||||
|
||||
**Why use a "slow interpreter" versus a "fast JIT"?**
|
||||
|
||||
In many situations, speed is not the main concern. Runtime executable size, memory usage, startup latency can be improved with the interpreter approach. Portability and security are much easier to achieve and maintain. Additionally, development impedance is much lower. A simple library like Wasm3 is easy to compile and integrate into an existing project. (Wasm3 builds in a just few seconds). Finally, on some platforms (i.e. iOS and WebAssembly itself) you can't generate executable code pages in runtime, so JIT is unavailable.
|
||||
|
||||
**Why would you want to run WASM on embedded devices?**
|
||||
|
||||
Wasm3 started as a research project and remains so by many means. Evaluating the engine in different environments is part of the research. Given that we have `Lua`, `JS`, `Python`, `Lisp`, `...` running on MCUs, `WebAssembly` is actually a promising alternative. It provides toolchain decoupling as well as a completely sandboxed, well-defined, predictable environment. Among practical use cases we can list `edge computing`, `scripting`, `plugin systems`, running `IoT rules`, `smart contracts`, etc.
|
||||
|
||||
## Used by
|
||||
|
||||
[<img src="https://wasmcloud.dev/images/logo.png" height="32" />](https://wasmcloud.dev/)
|
||||
[<img src="/extra/wowcube.png" height="32" />](https://wowcube.com/)
|
||||
[<img src="/extra/scailable.png" height="32" />](https://scailable.net/)
|
||||
[<img src="/extra/blynk.png" height="32" />](https://blynk.io/)
|
||||
[<img src="/extra/iden3.svg" height="32" />](https://www.iden3.io/)
|
||||
[<img src="https://user-images.githubusercontent.com/1506708/114701856-069ce700-9d2c-11eb-9b72-9ce2dfd9f0fb.png" height="32" />](https://github.com/kateinoigakukun/wasmic-ios)
|
||||
[<img src="https://www.balena.io/avatar.png" height="32" />](https://github.com/balena-io-playground/balena-wasm3)
|
||||
[<img src="https://krustlet.dev/images/horizontal.svg" height="32" />](https://github.com/deislabs/krustlet-wasm3)
|
||||
[<img src="/extra/shareup_app.svg" height="24" />](https://shareup.app/blog/introducing-shareup/)
|
||||
|
||||
## Further Resources
|
||||
|
||||
[Demos](./docs/Demos.md)
|
||||
[Installation instructions](./docs/Installation.md)
|
||||
[Cookbook](./docs/Cookbook.md)
|
||||
[Troubleshooting](./docs/Troubleshooting.md)
|
||||
[Build and Development instructions](./docs/Development.md)
|
||||
[Supported Hardware](./docs/Hardware.md)
|
||||
[Testing & Fuzzing](./docs/Testing.md)
|
||||
[Performance](./docs/Performance.md)
|
||||
[Interpreter Architecture](./docs/Interpreter.md)
|
||||
[Logging](./docs/Diagnostics.md)
|
||||
[Awesome WebAssembly Tools](https://github.com/vshymanskyy/awesome-wasm-tools/blob/main/README.md)
|
||||
|
||||
### License
|
||||
This project is released under The MIT License (MIT)
|
||||
|
||||
|
||||
[WasmProps]: https://github.com/WebAssembly/proposals/blob/master/finished-proposals.md "WebAssembly Finished Proposals"
|
|
@ -0,0 +1,462 @@
|
|||
# WASM module examples
|
||||
|
||||
### Rust WASI app
|
||||
|
||||
Create a new project:
|
||||
```sh
|
||||
$ cargo new --bin hello
|
||||
$ cd hello
|
||||
$ rustup target add wasm32-wasi
|
||||
```
|
||||
|
||||
Build and run:
|
||||
```sh
|
||||
$ cargo build --release --target wasm32-wasi
|
||||
|
||||
$ wasm3 ./target/wasm32-wasi/release/hello.wasm
|
||||
Hello, world!
|
||||
```
|
||||
|
||||
### AssemblyScript WASI app
|
||||
|
||||
Create `hello.ts`:
|
||||
```ts
|
||||
import "wasi"
|
||||
import {Console} from "as-wasi"
|
||||
|
||||
Console.log('Hello World!');
|
||||
```
|
||||
|
||||
Create `package.json`:
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"asbuild:debug": "asc hello.ts -b hello.wasm --debug",
|
||||
"asbuild:optimized": "asc hello.ts -b hello.wasm -O3s --noAssert",
|
||||
"asbuild:tiny": "asc hello.ts -b hello.wasm -O3z --noAssert --runtime stub --use abort=",
|
||||
"build": "npm run asbuild:optimized"
|
||||
},
|
||||
"devDependencies": {
|
||||
"assemblyscript": "*",
|
||||
"as-wasi": "*"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Build and run:
|
||||
```sh
|
||||
$ npm install
|
||||
$ npm run build
|
||||
|
||||
$ wasm3 hello.wasm
|
||||
Hello World!
|
||||
```
|
||||
|
||||
### TinyGo WASI app
|
||||
|
||||
Create `hello.go`:
|
||||
```go
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
fmt.Printf("Hello, %s!\n", "world")
|
||||
}
|
||||
```
|
||||
|
||||
Build and run:
|
||||
```sh
|
||||
$ tinygo build -o hello.wasm -target wasi -no-debug hello.go
|
||||
|
||||
$ wasm3 hello.wasm
|
||||
Hello, world!
|
||||
```
|
||||
|
||||
|
||||
### Go WASI app
|
||||
|
||||
Go currently does not provide the WASI interface.
|
||||
For reference see [this issue](https://github.com/golang/go/issues/31105).
|
||||
|
||||
|
||||
### Zig WASI app
|
||||
|
||||
Create `hello.zig`:
|
||||
```zig
|
||||
const std = @import("std");
|
||||
|
||||
pub fn main() !void {
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
try stdout.print("Hello, {s}!\n", .{"world"});
|
||||
}
|
||||
```
|
||||
|
||||
Build and run:
|
||||
```sh
|
||||
$ zig build-exe -O ReleaseSmall -target wasm32-wasi hello.zig
|
||||
|
||||
$ wasm3 hello.wasm
|
||||
Hello, world!
|
||||
```
|
||||
|
||||
### Zig C-code WASI app
|
||||
|
||||
Create `hello.c`:
|
||||
```c
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
printf("Hello, %s!\n", "world");
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
Build and run:
|
||||
```sh
|
||||
$ zig build-exe -O ReleaseSmall -target wasm32-wasi hello.c -lc
|
||||
|
||||
$ wasm3 hello.wasm
|
||||
Hello, world!
|
||||
```
|
||||
|
||||
## Zig library
|
||||
|
||||
Create `add.zig`:
|
||||
```zig
|
||||
export fn add(a: i32, b: i32) i64 {
|
||||
return a + b;
|
||||
}
|
||||
```
|
||||
|
||||
Build and run:
|
||||
```sh
|
||||
$ zig build-lib add.zig -O ReleaseSmall -target wasm32-freestanding
|
||||
|
||||
$ wasm3 --repl add.wasm
|
||||
wasm3> add 1 2
|
||||
Result: 3
|
||||
```
|
||||
|
||||
### C/C++ WASI app
|
||||
|
||||
The easiest way to start is to use [Wasienv](https://github.com/wasienv/wasienv).
|
||||
|
||||
Create `hello.cpp`:
|
||||
```cpp
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
std::cout << "Hello, world!" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
Or `hello.c`:
|
||||
```c
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
printf("Hello, %s!\n", "world");
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
Build and run:
|
||||
```sh
|
||||
$ wasic++ -O3 hello.cpp -o hello.wasm
|
||||
$ wasicc -O3 hello.c -o hello.wasm
|
||||
|
||||
$ wasm3 hello.wasm
|
||||
Hello World!
|
||||
```
|
||||
|
||||
Useful `clang` options:
|
||||
|
||||
- **-nostdlib** Do not use the standard system startup files or libraries when linking
|
||||
|
||||
- **-Wl,--no-entry** Do not output any entry point
|
||||
|
||||
- **-Wl,--export=\<value\>** Force a symbol to be exported, e.g. **-Wl,--export=foo** to export foo function
|
||||
|
||||
- **-Wl,--export-all** Export all symbols (normally combined with --no-gc-sections)
|
||||
|
||||
- **-Wl,--initial-memory=\<value\>** Initial size of the linear memory, which must be a multiple of 65536
|
||||
|
||||
- **-Wl,--max-memory=\<value\>** Maximum size of the linear memory, which must be a multiple of 65536
|
||||
|
||||
- **-z stack-size=\<vlaue\>** The auxiliary stack size, which is an area of linear memory, and must be smaller than initial memory size.
|
||||
|
||||
- **-Wl,--strip-all** Strip all symbols
|
||||
|
||||
- **-Wl,--shared-memory** Use shared linear memory
|
||||
|
||||
- **-Wl,--allow-undefined** Allow undefined symbols in linked binary
|
||||
|
||||
- **-Wl,--allow-undefined-file=\<value\>** Allow symbols listed in \<file\> to be undefined in linked binary
|
||||
|
||||
- **-pthread** Support POSIX threads in generated code
|
||||
|
||||
Limitations:
|
||||
- `setjmp/longjmp` and `C++ exceptions` are not available
|
||||
- no support for `threads` and `atomics`
|
||||
- no support for `dynamic libraries`
|
||||
|
||||
### Grain WASI app
|
||||
|
||||
Create `hello.gr`:
|
||||
```
|
||||
print("Hello, world!")
|
||||
```
|
||||
|
||||
Build and run:
|
||||
```sh
|
||||
$ grain compile hello.gr -o hello.wasm
|
||||
|
||||
$ wasm3 hello.wasm
|
||||
Hello, world!
|
||||
```
|
||||
|
||||
### WAT WASI app
|
||||
|
||||
Create `hello.wat`:
|
||||
```wat
|
||||
(module
|
||||
;; wasi_snapshot_preview1!fd_write(file_descriptor, *iovs, iovs_len, nwritten) -> status_code
|
||||
(import "wasi_snapshot_preview1" "fd_write" (func $fd_write (param i32 i32 i32 i32) (result i32)))
|
||||
|
||||
(memory 1)
|
||||
(export "memory" (memory 0))
|
||||
|
||||
;; Put a message to linear memory at offset 32
|
||||
(data (i32.const 32) "Hello, world!\n")
|
||||
|
||||
(func $main (export "_start")
|
||||
;; Create a new io vector at address 0x4
|
||||
(i32.store (i32.const 4) (i32.const 32)) ;; iov.iov_base - pointer to the start of the message
|
||||
(i32.store (i32.const 8) (i32.const 14)) ;; iov.iov_len - length of the message
|
||||
|
||||
(call $fd_write
|
||||
(i32.const 1) ;; file_descriptor - 1 for stdout
|
||||
(i32.const 4) ;; *iovs - pointer to the io vector
|
||||
(i32.const 1) ;; iovs_len - count of items in io vector
|
||||
(i32.const 20) ;; nwritten - where to store the number of bytes written
|
||||
)
|
||||
drop ;; discard the WASI status code
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
Build and run:
|
||||
```sh
|
||||
$ wat2wasm hello.wat -o hello.wasm
|
||||
|
||||
$ wasm3 hello.wasm
|
||||
Hello, world!
|
||||
```
|
||||
|
||||
### WAT library
|
||||
|
||||
Create `swap.wat`:
|
||||
```wat
|
||||
(module
|
||||
(func (export "swap") (param i32 i32) (result i32 i32)
|
||||
(get_local 1)
|
||||
(get_local 0)
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
Build and run:
|
||||
```sh
|
||||
$ wat2wasm swap.wat -o swap.wasm
|
||||
|
||||
$ wasm3 --repl swap.wasm
|
||||
wasm3> :invoke swap 123 456
|
||||
Result: 456:i32, 123:i32
|
||||
```
|
||||
|
||||
# Tracing
|
||||
|
||||
Drag'n'drop any of the WASI apps to [`WebAssembly.sh`](https://webassembly.sh/) and run:
|
||||
```sh
|
||||
$ wasm3-strace /tmp/hello.wasm
|
||||
```
|
||||
|
||||
The execution trace will be produced:
|
||||
```js
|
||||
_start () {
|
||||
__wasilibc_init_preopen () {
|
||||
malloc (i32: 16) {
|
||||
dlmalloc (i32: 16) {
|
||||
sbrk (i32: 0) {
|
||||
} = 131072
|
||||
sbrk (i32: 65536) {
|
||||
```
|
||||
<details>
|
||||
<summary>Click to expand!</summary>
|
||||
|
||||
```js
|
||||
} = 131072
|
||||
} = 131088
|
||||
} = 131088
|
||||
calloc (i32: 24, i32: 0) {
|
||||
dlmalloc (i32: 96) {
|
||||
} = 131120
|
||||
memset (i32: 131120, i32: 65504, i32: 0) {
|
||||
} = 131120
|
||||
} = 131120
|
||||
po_map_assertvalid (i32: 131088) {
|
||||
}
|
||||
po_map_assertvalid (i32: 131088) {
|
||||
}
|
||||
}
|
||||
wasi_unstable!fd_prestat_get(3, 65528) { <native> } = 0
|
||||
malloc (i32: 2) {
|
||||
dlmalloc (i32: 2) {
|
||||
} = 131232
|
||||
} = 131232
|
||||
wasi_unstable!fd_prestat_dir_name(3, 131232, 1) { <native> } = 0
|
||||
__wasilibc_register_preopened_fd (i32: 3, i32: 131120) {
|
||||
po_map_assertvalid (i32: 131088) {
|
||||
}
|
||||
po_map_assertvalid (i32: 131088) {
|
||||
}
|
||||
strdup (i32: 131232) {
|
||||
strlen (i32: 131232) {
|
||||
} = 1
|
||||
malloc (i32: 2) {
|
||||
dlmalloc (i32: 2) {
|
||||
} = 131248
|
||||
} = 131248
|
||||
memcpy (i32: 131248, i32: 131233, i32: 131232) {
|
||||
} = 131248
|
||||
} = 131248
|
||||
wasi_unstable!fd_fdstat_get(3, 65496) { <native> } = 0
|
||||
po_map_assertvalid (i32: 131088) {
|
||||
}
|
||||
po_map_assertvalid (i32: 131088) {
|
||||
}
|
||||
} = 0
|
||||
free (i32: 131232) {
|
||||
dlfree (i32: 131232) {
|
||||
}
|
||||
}
|
||||
wasi_unstable!fd_prestat_get(4, 65528) { <native> } = 0
|
||||
malloc (i32: 2) {
|
||||
dlmalloc (i32: 2) {
|
||||
} = 131232
|
||||
} = 131232
|
||||
wasi_unstable!fd_prestat_dir_name(4, 131232, 1) { <native> } = 0
|
||||
__wasilibc_register_preopened_fd (i32: 4, i32: 131120) {
|
||||
po_map_assertvalid (i32: 131088) {
|
||||
}
|
||||
po_map_assertvalid (i32: 131088) {
|
||||
}
|
||||
strdup (i32: 131232) {
|
||||
strlen (i32: 131232) {
|
||||
} = 1
|
||||
malloc (i32: 2) {
|
||||
dlmalloc (i32: 2) {
|
||||
} = 131264
|
||||
} = 131264
|
||||
memcpy (i32: 131264, i32: 131233, i32: 131232) {
|
||||
} = 131264
|
||||
} = 131264
|
||||
wasi_unstable!fd_fdstat_get(4, 65496) { <native> } = 0
|
||||
po_map_assertvalid (i32: 131088) {
|
||||
}
|
||||
po_map_assertvalid (i32: 131088) {
|
||||
}
|
||||
} = 0
|
||||
free (i32: 131232) {
|
||||
dlfree (i32: 131232) {
|
||||
}
|
||||
}
|
||||
wasi_unstable!fd_prestat_get(5, 65528) { <native> } = 8
|
||||
__wasm_call_ctors () {
|
||||
}
|
||||
__original_main () {
|
||||
printf (i32: 65536, i32: 0) {
|
||||
vfprintf (i32: 69512, i32: 0, i32: 65536) {
|
||||
printf_core (i32: 0, i32: -1, i32: 65536, i32: -16, i32: 65480) {
|
||||
} = 0
|
||||
__towrite (i32: 69512) {
|
||||
} = 0
|
||||
printf_core (i32: 69512, i32: 8, i32: 65536, i32: -1, i32: 65480) {
|
||||
__fwritex (i32: 65536, i32: 0, i32: 7) {
|
||||
memcpy (i32: 68472, i32: 0, i32: 65536) {
|
||||
} = 68472
|
||||
} = 7
|
||||
__fwritex (i32: 65543, i32: 0, i32: 0) {
|
||||
memcpy (i32: 68479, i32: 0, i32: 65543) {
|
||||
} = 68479
|
||||
} = 0
|
||||
pop_arg (i32: 64456, i32: 0, i32: 9) {
|
||||
}
|
||||
strnlen (i32: 65548, i32: 0) {
|
||||
memchr (i32: 65548, i32: 4, i32: 0) {
|
||||
} = 65553
|
||||
} = 5
|
||||
__fwritex (i32: 67222, i32: 65553, i32: 0) {
|
||||
memcpy (i32: 68479, i32: 0, i32: 67222) {
|
||||
} = 68479
|
||||
} = 0
|
||||
__fwritex (i32: 65548, i32: 65553, i32: 5) {
|
||||
memcpy (i32: 68479, i32: 0, i32: 65548) {
|
||||
} = 68479
|
||||
} = 5
|
||||
__fwritex (i32: 65545, i32: 0, i32: 2) {
|
||||
__stdout_write (i32: 69512, i32: 0, i32: 65545) {
|
||||
__isatty (i32: 1) {
|
||||
wasi_unstable!fd_fdstat_get(1, 64376) { <native> } = 0
|
||||
} = 1
|
||||
__stdio_write (i32: 69512, i32: 64368, i32: 65545) {
|
||||
writev (i32: 1, i32: -16, i32: 64384) {
|
||||
Hello, world!
|
||||
wasi_unstable!fd_write(1, 64384, 2, 64380) { <native> } = 0
|
||||
} = 14
|
||||
} = 2
|
||||
} = 2
|
||||
memcpy (i32: 68472, i32: -1, i32: 65547) {
|
||||
} = 68472
|
||||
} = 2
|
||||
} = 14
|
||||
} = 14
|
||||
} = 14
|
||||
} = 0
|
||||
__prepare_for_exit () {
|
||||
dummy () {
|
||||
}
|
||||
__stdio_exit () {
|
||||
__ofl_lock () {
|
||||
} = 69504
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
# Gas Metering
|
||||
|
||||
```sh
|
||||
$ npm install -g wasm-metering
|
||||
|
||||
$ wasm-meter hello.wasm hello-metered.wasm
|
||||
|
||||
$ wasm3 hello-metered.wasm
|
||||
Warning: Gas is limited to 500000000.0000
|
||||
Hello, world!
|
||||
Gas used: 20.8950
|
||||
|
||||
$ wasm3 --gas-limit 10 hello-metered.wasm
|
||||
Warning: Gas is limited to 10.0000
|
||||
Gas used: 10.0309
|
||||
Error: [trap] Out of gas
|
||||
```
|
||||
|
||||
# Other resources
|
||||
|
||||
- [WebAssembly by examples](https://wasmbyexample.dev/home.en-us.html) by Aaron Turner
|
||||
- [Writing WebAssembly](https://docs.wasmtime.dev/wasm.html) in Wasmtime docs
|
|
@ -0,0 +1,17 @@
|
|||
# Wasm3 demos
|
||||
|
||||
- **In-browser Wasm3 (with MetaWASI support) on Webassembly.sh** │ [try it](https://webassembly.sh/?run-command=wasm3)
|
||||
- **PyGame + pywasm3 examples** | [github](https://github.com/wasm3/pywasm3/tree/main/examples)
|
||||
- **DOOM compiled to WASI, running on pywasm3** | [video](https://twitter.com/wasm3_engine/status/1393588527863144450), [github](https://github.com/wasm3/pywasm3-doom-demo)
|
||||
- **WebAssembly On Your Nintendo DS** | [blog](https://softwayre.com/blog/2021/09/13/webassembly-on-your-nintendo-ds), [github](https://github.com/moxon6/snake-assemblyscript-ds)
|
||||
- **Each pixel on this ESP32 board is controlled by a different WebAssembly app** | [video](https://twitter.com/zubr_kabbi/status/1436833749359017985), [github](https://github.com/kabbi/m5-atom-wasms)
|
||||
- **Wasm3 self-compilation using `clang.wasm`** | [github](https://github.com/wasm3/wasm3-self-compiling)
|
||||
- **Dino game**
|
||||
- on PyBadge/Arduino | [video](https://twitter.com/vshymanskyy/status/1345048053041029121), [github](https://github.com/wasm3/wasm3-arduino/tree/main/examples/Wasm_Dino_PyBadge)
|
||||
- on Raspberry Pi Pico | [github](https://github.com/vshymanskyy/wasm3_dino_rpi_pico)
|
||||
- on ESP32 TTGO TDisplay | [github](https://github.com/wasm3/wasm3-arduino/tree/main/examples/Wasm_Dino_ESP32_TDisplay)
|
||||
- **Basic WiFi/Gpio access, updating `wasm` file over-the-air** │ [video](https://twitter.com/alvaroviebrantz/status/1221618910803513344), [github](https://github.com/alvarowolfx/wasm-arduino-wifi) [ESP32 / Web]
|
||||
- **RGB lamp animation using WebAssembly** │ [video](https://twitter.com/wasm3_engine/status/1222835097289752576), [github](https://github.com/vshymanskyy/Wasm3_RGB_Lamp) [nRF51 / nRF52 / ESP8266 / ESP32]
|
||||
- **LCD display rendering with AssemblyScript** │ [video](https://twitter.com/h1romas4/status/1228581467850100736), [github](https://github.com/h1romas4/m5stack-wasm3-testing) [M5Stack / ESP32]
|
||||
- **Conway's Game Of Life with AssemblyScript** │ [video](https://www.youtube.com/watch?v=Hc2sbhGMrig), [github](https://github.com/h1romas4/maixduino-wasm3-testing) [Maixduino / K210]
|
||||
|
|
@ -0,0 +1,181 @@
|
|||
# Wasm3 development notes
|
||||
|
||||
This project uses CMake.
|
||||
General build steps look like:
|
||||
```sh
|
||||
mkdir -p build
|
||||
cd build
|
||||
cmake ..
|
||||
make -j8
|
||||
```
|
||||
|
||||
Wasm3 is continuously tested with Clang, GCC, MSVC compilers, and on multiple platforms.
|
||||
It can be easily integrated into any build system, as shown in `platforms`.
|
||||
|
||||
## Build on Linux, OS X
|
||||
|
||||
### Clang
|
||||
|
||||
```sh
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -GNinja -DCLANG_SUFFIX="-12" ..
|
||||
ninja
|
||||
```
|
||||
|
||||
### GCC
|
||||
|
||||
```sh
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -GNinja ..
|
||||
ninja
|
||||
```
|
||||
|
||||
## Build on Windows
|
||||
|
||||
Prerequisites:
|
||||
- [Build Tools for Visual Studio 2019](https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2019)
|
||||
- [CMake](https://cmake.org/download/)
|
||||
- [Ninja](https://github.com/ninja-build/ninja/releases)
|
||||
- [Clang 9](https://releases.llvm.org/download.html#9.0.0)
|
||||
|
||||
Recommended tools:
|
||||
- [Cmder](https://cmder.net/)
|
||||
- [Python3](https://www.python.org/downloads/)
|
||||
- [ptime](http://www.pc-tools.net/win32/ptime/)
|
||||
|
||||
```sh
|
||||
# Prepare environment (if needed):
|
||||
"C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Auxiliary\Build\vcvars64.bat"
|
||||
set PATH=C:\Python36-32\;C:\Python36-32\Scripts\;%PATH%
|
||||
```
|
||||
|
||||
### Build with MSBuild
|
||||
|
||||
```sh
|
||||
# Create a build directory as usual, then build a specific configuration
|
||||
mkdir build
|
||||
cd build
|
||||
|
||||
# Configure Clang, x64
|
||||
cmake -G"Visual Studio 16 2019" -A x64 -T ClangCL ..
|
||||
|
||||
# Configure Clang, x86
|
||||
cmake -G"Visual Studio 16 2019" -A Win32 -T ClangCL ..
|
||||
|
||||
# Configure MSVC, x64
|
||||
cmake -G"Visual Studio 16 2019" -A x64 ..
|
||||
|
||||
# Configure MSVC, x86
|
||||
cmake -G"Visual Studio 16 2019" -A Win32 ..
|
||||
|
||||
# Build
|
||||
cmake --build . --config Release
|
||||
cp ./Release/wasm3.exe ./
|
||||
```
|
||||
|
||||
### Build with Ninja
|
||||
|
||||
```sh
|
||||
set PATH=C:\Program Files\CMake\bin;%PATH%
|
||||
set PATH=C:\Program Files\LLVM\bin;%PATH%
|
||||
|
||||
# Clang
|
||||
cmake -GNinja -DCLANG_CL=1 ..
|
||||
ninja
|
||||
|
||||
# MSVC
|
||||
cmake -GNinja ..
|
||||
ninja
|
||||
```
|
||||
|
||||
## Build using compiler directly
|
||||
|
||||
This can be useful for cross-compilation, quick builds or when a build system (CMake, Ninja, etc.) is not available.
|
||||
|
||||
### gcc/clang
|
||||
```sh
|
||||
gcc -O3 -g0 -s -Isource -Dd_m3HasWASI source/*.c platforms/app/main.c -lm -o wasm3
|
||||
```
|
||||
|
||||
### msvc/clang-cl
|
||||
```sh
|
||||
cl source/*.c platforms/app/main.c /Isource /MD /Ox /Oy /Gw /GS- /W0 /Dd_m3HasWASI /Fewasm3.exe /link advapi32.lib
|
||||
```
|
||||
|
||||
### mingw-w64
|
||||
```sh
|
||||
x86_64-w64-mingw32-gcc -O3 -g0 -s -Isource -Dd_m3HasWASI source/*.c platforms/app/main.c -lm -lpthread -static -o wasm3.exe
|
||||
```
|
||||
|
||||
## Build for microcontrollers
|
||||
|
||||
In `./platforms/` folder you can find projects for different targets. Some of them are using Platformio, so you can follow the regular pio build process. Others have custom instructions in respective `README.md` files.
|
||||
|
||||
## Build with WasiEnv
|
||||
|
||||
```sh
|
||||
wasimake cmake -GNinja ..
|
||||
ninja
|
||||
```
|
||||
|
||||
If you want to enable experimental WASM features during the build:
|
||||
|
||||
```sh
|
||||
export CFLAGS="-Xclang -target-feature -Xclang +tail-call"
|
||||
wasimake cmake -GNinja ..
|
||||
ninja
|
||||
```
|
||||
|
||||
Here's how some options can be used with different tools:
|
||||
|
||||
```log
|
||||
Clang target-feature option WABT option Chromium --js-flags option
|
||||
----------------------------------------------------------------------------------------------------------------
|
||||
+multivalue --enable-multi-value --experimental-wasm-mv
|
||||
+tail-call --enable-tail-call --experimental-wasm-return-call
|
||||
+bulk-memory --enable-bulk-memory --experimental-wasm-bulk-memory
|
||||
+nontrapping-fptoint --enable-saturating-float-to-int --experimental-wasm-sat-f2i-conversions
|
||||
+sign-ext --enable-sign-extension --experimental-wasm-se
|
||||
+simd128, +unimplemented-simd128 --enable-simd --experimental-wasm-simd
|
||||
```
|
||||
|
||||
```sh
|
||||
# List clang options:
|
||||
llc -march=wasm32 -mattr=help
|
||||
|
||||
# List Chromium options:
|
||||
chromium-browser --single-process --js-flags="--help" 2>&1 | grep wasm
|
||||
```
|
||||
|
||||
## Build with Zig
|
||||
|
||||
Grab the latest nightly Zig toolchain from [ziglang.org/download], and then simply run:
|
||||
|
||||
[ziglang.org/download]: https://ziglang.org/download/
|
||||
|
||||
```sh
|
||||
zig build
|
||||
```
|
||||
|
||||
This will install `wasm3` compiled for your target architecture in `zig-out/bin/wasm3`.
|
||||
|
||||
In order to build `wasm3` in a mode different than Debug, e.g., in ReleaseFast, pass in
|
||||
an appropriate flag like so:
|
||||
|
||||
```sh
|
||||
zig build -Drelease-fast
|
||||
```
|
||||
|
||||
If you want to cross-compile to some specific target, pass in the target with a flag like so:
|
||||
|
||||
```sh
|
||||
zig build -Dtarget=wasm32-wasi
|
||||
```
|
||||
|
||||
Or if targeting Apple Silicon (this works from *any* host with Zig):
|
||||
|
||||
```sh
|
||||
zig build -Dtarget=aarch64-macos
|
||||
```
|
|
@ -0,0 +1,57 @@
|
|||
|
||||
## Logs
|
||||
|
||||
To enable various logs, modify the defines in `m3_config.h`. These are only enabled when compiled in debug mode.
|
||||
|
||||
```C
|
||||
# define d_m3LogParse 0 // .wasm binary decoding info
|
||||
# define d_m3LogModule 0 // Wasm module info
|
||||
# define d_m3LogCompile 0 // wasm -> metacode generation phase
|
||||
# define d_m3LogWasmStack 0 // dump the wasm stack when pushed or popped
|
||||
# define d_m3LogEmit 0 // metacode-generation info
|
||||
# define d_m3LogCodePages 0 // dump metacode pages when released
|
||||
# define d_m3LogRuntime 0 // higher-level runtime information
|
||||
# define d_m3LogNativeStack 0 // track the memory usage of the C-stack
|
||||
```
|
||||
|
||||
|
||||
## Operation Profiling
|
||||
|
||||
To profile the interpreter's operations enable `d_m3EnableOpProfiling` in `m3_config.h`. This profiling option works in either release or debug builds.
|
||||
|
||||
When a runtime is released or `m3_PrintProfilerInfo ()` is called, a table of the executed operations and
|
||||
their instance counts will be printed to stderr.
|
||||
|
||||
```
|
||||
23199904 op_SetSlot_i32
|
||||
12203917 op_i32_Add_ss
|
||||
6682992 op_u32_GreaterThan_sr
|
||||
2021555 op_u32_ShiftLeft_sr
|
||||
1136577 op_u32_ShiftLeft_ss
|
||||
1019725 op_CopySlot_32
|
||||
775431 op_i32_Subtract_ss
|
||||
703307 op_i32_Store_i32_rs
|
||||
337656 op_i32_Multiply_rs
|
||||
146383 op_u32_Or_rs
|
||||
99168 op_u64_Or_rs
|
||||
50311 op_u32_ShiftRight_rs
|
||||
43319 op_u32_ShiftLeft_rs
|
||||
21104 op_i64_Load_i64_s
|
||||
17450 op_i32_LessThan_sr
|
||||
7129 op_If_s
|
||||
5574 op_i32_Wrap_i64_r
|
||||
1630 op_f64_Load_f64_r
|
||||
1116 op_u32_Divide_sr
|
||||
903 op_i32_GreaterThan_ss
|
||||
390 op_u64_And_rs
|
||||
108 op_Select_f64_rss
|
||||
77 op_u64_ShiftLeft_sr
|
||||
64 op_Select_i32_ssr
|
||||
11 op_f64_Store_f64_ss
|
||||
10 op_MemGrow
|
||||
8 op_f64_GreaterThan_sr
|
||||
4 op_u64_LessThan_rs
|
||||
1 op_u32_Xor_ss
|
||||
1 op_i64_Subtract_ss
|
||||
```
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
# Wasm3 hardware support
|
||||
|
||||
## Recommended devices (ideal for beginners)
|
||||
|
||||
- ESP32-based
|
||||
- **LilyGO TTGO T7 V1.4** │ [AliExpress](https://www.aliexpress.com/item/32977375539.html)
|
||||
- **M5Stack Fire** │ [AliExpress](https://www.aliexpress.com/item/32847906756.html)
|
||||
- nRF52840-based
|
||||
- **Adafruit CLUE** │ [Adafruit](https://www.adafruit.com/clue)
|
||||
- **Arduino Nano 33 BLE (or BLE Sense)** │ [Arduino](https://store.arduino.cc/arduino-nano-33-ble)
|
||||
- **Particle Argon** │ [Particle](https://store.particle.io/collections/bluetooth/products/argon)
|
||||
- **Adafruit Feather nRF52840** | [Adafruit](https://www.adafruit.com/product/4062)
|
||||
- Other
|
||||
- **Raspberry Pi Pico** | [Raspberry Pi](https://www.raspberrypi.org/products/raspberry-pi-pico)
|
||||
- **Adafruit PyGamer/PyBadge/PyBadge LC** │ [Adafruit](https://www.adafruit.com/product/4242)
|
||||
- **SparkFun Artemis** | [SparkFun](https://www.sparkfun.com/search/results?term=Artemis)
|
||||
- **Teensy 4.0** │ [PJRC](https://www.pjrc.com/store/teensy40.html)
|
||||
- **Wemos W600 PICO** │ [AliExpress](https://www.aliexpress.com/item/4000314757449.html)
|
||||
|
||||
## Compatibility table
|
||||
|
||||
Device | Chipset | Architecture | Clock | Flash | RAM
|
||||
--- |:---: | --- | -----:| --- | ---
|
||||
Espressif ESP32 | | Xtensa LX6 <sup>⚠️</sup> | 240MHz | 4 MB | 520KB
|
||||
Particle Argon, Boron, Xenon | nRF52840 | Cortex-M4F | 64MHz | 1 MB | 256KB
|
||||
Particle Photon, Electron | STM32F205 | Cortex-M3 | 120Mhz | 1 MB | 128KB
|
||||
Sparkfun Photon RedBoard | STM32F205 | Cortex-M3 | 120Mhz | 1 MB | 128KB
|
||||
Air602 | WM W600 | Cortex-M3 | 80MHz | 1 MB | 160KB+128KB
|
||||
Adafruit PyBadge | ATSAMD51J19 | Cortex-M4F | 120MHz | 512KB | 192KB
|
||||
Realtek RTL8711 | | Cortex-M3 | 166MHz | 2 MB | 2 MB+512KB
|
||||
Nordic nRF52840 | | Cortex-M4F | 64MHz | 1 MB | 256KB
|
||||
Nordic nRF52833 | | Cortex-M4F | 64MHz | 512KB | 128KB
|
||||
P-Nucleo WB55RG | STM32WB55RG | Cortex-M4F | 64MHz | 1 MB | 256KB
|
||||
Teensy 4.0 | NXP iMXRT1062 | Cortex-M7 | 600MHz | 2 MB | 1 MB
|
||||
Teensy 3.5 | MK64FX512 | Cortex-M4F | 120MHz | 512KB | 192KB
|
||||
MXChip AZ3166 | EMW3166 | Cortex-M4 | 100MHz | 1 MB+2 MB | 256KB
|
||||
Arduino Due | AT91SAM3X8E | Cortex-M3 | 84MHz | 512KB | 96KB
|
||||
Sipeed MAIX | Kendryte K210 | RV64IMAFDC | 400MHz | 16 MB | 8 MB
|
||||
Fomu (soft CPU) | Lattice ICE40UP5K | RV32I | 12MHz | 2 MB | 128KB
|
||||
|
||||
## Limited support
|
||||
|
||||
The following devices can run Wasm3, however they cannot afford to allocate even a single Linear Memory page (64KB).
|
||||
This means `memoryLimit` should be set to the actual amount of RAM available, and that in turn usually breaks the allocator of the hosted Wasm application (which still assumes the page is 64KB and performs OOB access).
|
||||
|
||||
Device | Chipset | Architecture | Clock | Flash | RAM
|
||||
--- |:---: | --- | -----:| --- | ---
|
||||
Espressif ESP8266 | | Xtensa L106 <sup>⚠️</sup> | 160MHz | 4 MB | ~50KB (available)
|
||||
Teensy 3.1/3.2 | NXP MK20DX256 | Cortex-M4 | 72MHz | 288KB | 64KB
|
||||
Blue Pill | STM32F103 | Cortex-M3 | 72MHz | 64KB | 20KB
|
||||
Arduino MKR* | SAMD21 | Cortex-M0+ <sup>⚠️</sup> | 48MHz | 256KB | 32KB
|
||||
Arduino 101 | Intel Curie | ARC32 | 32MHz | 196KB | 24KB
|
||||
SiFive HiFive1 | Freedom E310 | RV32IMAC | 320MHz | 16 MB | 16KB
|
||||
Nordic nRF52832 | | Cortex-M4F | 64MHz | 256/512KB | 32/64KB
|
||||
Nordic nRF51822 | | Cortex-M0 <sup>⚠️</sup> | 16MHz | 128/256KB | 16/32KB
|
||||
Wicked Device WildFire | ATmega1284 | 8-bit AVR <sup>⚠️</sup> | 20MHz | 128KB | 16KB
|
||||
|
||||
### Legend:
|
||||
⚠️ This architecture/compiler currently fails to perform TCO (Tail Call Optimization/Elimination), which leads to sub-optimal interpreter behaviour (intense native stack usage, lower performance).
|
||||
There are plans to improve this in future 🦄.
|
|
@ -0,0 +1,26 @@
|
|||
# Wasm3 installation
|
||||
|
||||
## Windows
|
||||
Download `wasm3-win-x64.exe` or `wasm3-win-x86.exe` from the [latest release page](https://github.com/wasm3/wasm3/releases/latest).
|
||||
|
||||
## macOS
|
||||
Use [Homebrew](https://brew.sh) to build and install automatically:
|
||||
```sh
|
||||
brew install wasm3
|
||||
```
|
||||
|
||||
## Cosmopolitan / Actually Portable Executable
|
||||
[APE](https://justine.lol/cosmopolitan/index.html) is a polyglot format that runs natively on Linux + Mac + Windows + FreeBSD + OpenBSD + NetBSD + BIOS.
|
||||
Download `wasm3-cosmopolitan.com` from the [latest release page](https://github.com/wasm3/wasm3/releases/latest)
|
||||
|
||||
## OpenWRT
|
||||
Follow instructions [here](https://github.com/wasm3/wasm3-openwrt-packages).
|
||||
|
||||
## Arduino / PlatformIO / Particle.io library
|
||||
Find `Wasm3` in the `Library Manager` and install it.
|
||||
|
||||
## Python module
|
||||
```sh
|
||||
pip3 install pywasm3
|
||||
```
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
# M3
|
||||
|
||||
This is a WebAssembly interpreter written in C using a novel, high performance interpreter topology. The interpreter strategy (named M3) was developed prior to this particular Wasm project and is described some below.
|
||||
|
||||
## Purpose
|
||||
|
||||
I don't know. I just woke up one day and started hacking this out after realizing my interpreter was well suited for the Wasm bytecode structure. Some ideas:
|
||||
|
||||
* It could be useful for embedded systems.
|
||||
* It might be a good start-up, pre-JIT interpreter in a more complex Wasm compiler system.
|
||||
* It could serve as a Wasm validation library.
|
||||
* The interpreter topology might be inspiring to others.
|
||||
|
||||
|
||||
## Benchmarks
|
||||
|
||||
It's at least fleshed out enough to run some simple benchmarks.
|
||||
|
||||
Tested on a 4 GHz Intel Core i7 iMac (Retina 5K, 27-inch, Late 2014). M3 was compiled with Clang -Os. C benchmarks were compiled with gcc with -O3
|
||||
|
||||
### Mandelbrot
|
||||
|
||||
C code lifted from: https://github.com/ColinEberhardt/wasm-mandelbrot
|
||||
|
||||
|Interpreter|Execution Time|Relative to GCC| |
|
||||
|-----------|--------------|---------------|----|
|
||||
|Life |547 s |133 x | https://github.com/perlin-network/life
|
||||
|Lua |122 s |30 x | This isn't Lua running some weird Wasm transcoding; a manual Lua conversion of the C benchmark as an additional reference point.
|
||||
|M3 |17.9 s |4.4 x | (3.7 x on my 2016 MacBook Pro)
|
||||
|GCC |4.1 s | |
|
||||
|
||||
### CRC32
|
||||
|
||||
|Interpreter|Execution Time|Relative to GCC|
|
||||
|-----------|--------------|---------------|
|
||||
|Life |153 s |254 x |
|
||||
|M3 |5.1 s |8.5 x |
|
||||
|GCC |600 ms | |
|
||||
|
||||
|
||||
In general, the M3 strategy seems capable of executing code around 4-15X slower than compiled code on a modern x86 processor. (Older CPUs don't fare as well. I suspect branch predictor differences.) I have yet to test on anything ARM.
|
||||
|
||||
## M3: Massey Meta Machine
|
||||
|
||||
Over the years, I've mucked around with creating my own personal programming language. It's called Gestalt. The yet unreleased repository will be here: https://github.com/soundandform/gestalt
|
||||
|
||||
Early on I decided I needed an efficient interpreter to achieve the instant-feedback, live-coding environment I desire. Deep traditional compilation is too slow and totally unnecessary during development. And, most importantly, compilation latency destroys creative flow.
|
||||
|
||||
I briefly considered retooling something extant. The Lua virtual machine, one of the faster interpreters, is too Lua centric. And everything else is too slow.
|
||||
|
||||
I've also always felt that the "spin in a loop around a giant switch statement" thing most interpreters do was clumsy and boring. My intuition said there was something more elegant to be found.
|
||||
|
||||
The structure that emerged I named a "meta machine" since it mirrors the execution of compiled code much more closely than the switch-based virtual machine.
|
||||
|
||||
I set out with a goal of approximating Lua's performance. But this interpreter appears to beat Lua by 3X and more on basic benchmarks -- whatever they're worth!
|
||||
|
||||
### How it works
|
||||
|
||||
This rough information might not be immediately intelligible without referencing the source code.
|
||||
|
||||
#### Reduce bytecode decoding overhead
|
||||
|
||||
* Bytecode/opcodes are translated into more efficient "operations" during a compilation pass, generating pages of meta-machine code
|
||||
* M3 trades some space for time. Opcodes map to up to 3 different operations depending on the number of source operands and commutative-ness.
|
||||
* Commonly occurring sequences of operations can can also be optimized into a "fused" operation. This *sometimes* results in improved performance.
|
||||
* the modern CPU pipeline is a mysterious beast
|
||||
* In M3/Wasm, the stack machine model is translated into a more direct and efficient "register file" approach.
|
||||
|
||||
#### Tightly Chained Operations
|
||||
|
||||
* M3 operations are C functions with a single, fixed function signature.
|
||||
|
||||
```C
|
||||
void * Operation_Whatever (pc_t pc, u64 * sp, u8 * mem, reg_t r0, f64 fp0);
|
||||
```
|
||||
|
||||
* The arguments of the operation are the M3's virtual machine registers
|
||||
* program counter, stack pointer, etc.
|
||||
* The return argument is a trap/exception and program flow control signal
|
||||
* The M3 program code is traversed by each operation calling the next. The operations themselves drive execution forward. There is no outer control structure.
|
||||
* Because operations end with a call to the next function, the C compiler will tail-call optimize most operations.
|
||||
* Finally, note that x86/ARM calling conventions pass initial arguments through registers, and the indirect jumps between operations are branch predicted.
|
||||
|
||||
#### The End Result
|
||||
|
||||
Since operations all have a standardized signature and arguments are tail-call passed through to the next, the M3 "virtual" machine registers end up mapping directly to real CPU registers. It's not virtual! Instead, it's a meta machine with very low execution impedance.
|
||||
|
||||
|M3 Register |x86 Register|
|
||||
|-----------------------------|------------|
|
||||
|program counter (pc) |rdi |
|
||||
|stack pointer (sp) |rsi |
|
||||
|linear memory (mem) |rdx |
|
||||
|integer register (r0) |rcx |
|
||||
|floating-point register (fp0)|xmm0 |
|
||||
|
||||
|
||||
For example, here's a bitwise-or operation in the M3 compiled on x86.
|
||||
|
||||
```assembly
|
||||
m3`op_u64_Or_sr:
|
||||
0x1000062c0 <+0>: movslq (%rdi), %rax ; load operand stack offset
|
||||
0x1000062c3 <+3>: orq (%rsi,%rax,8), %rcx ; or r0 with stack operand
|
||||
0x1000062c7 <+7>: movq 0x8(%rdi), %rax ; fetch next operation
|
||||
0x1000062cb <+11>: addq $0x10, %rdi ; increment program counter
|
||||
0x1000062cf <+15>: jmpq *%rax ; jump to next operation
|
||||
```
|
||||
|
||||
|
||||
#### Registers and Operational Complexity
|
||||
|
||||
* The conventional Windows calling convention isn't compatible with M3, as-is, since it only passes 4 arguments through registers. Applying the vectorcall calling convention (https://docs.microsoft.com/en-us/cpp/cpp/vectorcall) resolves this problem.
|
||||
|
||||
* It's possible to use more CPU registers. For example, adding an additional floating-point register to the meta-machine did marginally increase performance in prior tests. However, the operation space increases exponentially. With one register, there are up to 3 operations per opcode (e.g. a non-commutative math operation). Adding another register increases the operation count to 10. However, as you can see above, operations tend to be tiny.
|
||||
|
||||
#### Stack Usage
|
||||
|
||||
* Looking at the above assembly code, you'll notice that once an M3 operation is optimized, it doesn't need the regular stack (no mucking with the ebp/esp registers). This is the case for 90% of the opcodes. Branching and call operations do require stack variables. Therefore, execution can't march forward indefinitely; the stack would eventually overflow.
|
||||
|
||||
Therefore, loops unwind the stack. When a loop is continued, the Continue operation returns, unwinding the stack. Its return value is a pointer to the loop opcode it wants to unwind to. The Loop operations checks for its pointer and responds appropriately, either calling back into the loop code or returning the loop pointer back down the call stack.
|
||||
|
||||
* Traps/Exceptions work similarly. A trap pointer is returned from the trap operation which has the effect of unwinding the entire stack.
|
||||
|
||||
* Returning from a Wasm function also unwinds the stack, back to the point of the Call operation.
|
||||
|
||||
* But, because M3 execution leans heavily on the native stack, this does create one runtime usage issue.
|
||||
|
||||
A conventional interpreter can save its state, break out of its processing loop and return program control to the client code. This is not the case in M3 since the C stack might be wound up in a loop for long periods of time.
|
||||
|
||||
With Gestalt, I resolved this problem with fibers (built with Boost Context). M3 execution occurs in a fiber so that control can effortlessly switch to the "main" fiber. No explicit saving of state is necessary since that's the whole purpose of a fiber.
|
||||
|
||||
More simplistically, the interpreter runtime can also periodically call back to the client (in the either the Loop or LoopContinue operation). This is necessary, regardless, to detect hangs and break out of infinite loops.
|
||||
|
||||
|
||||
## The M3 strategy for other languages (is rad)
|
||||
|
||||
The Gestalt M3 interpreter works slightly differently than this Wasm version. With Gestalt, blocks of all kind (if/else/try), not just loops, unwind the native stack. (This somewhat degrades raw x86 performance.)
|
||||
|
||||
But, this adds a really beautiful property to the interpreter. The lexical scoping of a block in the language source code maps directly into the interpreter. All opcodes/operations end up having an optional prologue/epilogue structure. This made things like reference-counting objects in Gestalt effortless. Without this property, the compiler itself would have to track scope and insert dererence opcodes intentionally. Instead, the "CreateObject" operation is also the "DestroyObject" operation on the exit/return pathway.
|
||||
|
||||
Here's some pseudocode to make this more concrete:
|
||||
|
||||
```
|
||||
return_t Operation_NewObject (registers...)
|
||||
{
|
||||
Object o = runtime->CreateObject (registers...);
|
||||
|
||||
* stack [index] = o;
|
||||
|
||||
return_t r = CallNextOperation (registers...); // executes to the end of the scope/block/curly-brace & returns
|
||||
|
||||
if (o->ReferenceCount () == 0)
|
||||
runtime->DestroyObject (registers..., o); // calls o's destructor and frees memory
|
||||
|
||||
return r;
|
||||
}
|
||||
```
|
||||
|
||||
Likewise, a "defer" function (like in Go) becomes absolutely effortless to implement. Exceptions (try/catch) as well.
|
||||
|
||||
|
||||
## Prior Art
|
||||
|
||||
After the Wasm3 project was posted to Hacker News (https://news.ycombinator.com/item?id=22024758), I finally discovered precedent for this tail-call interpreter design. It has previously been called "threaded code". See the "Continuation-passing style" section: http://www.complang.tuwien.ac.at/forth/threaded-code.html).
|
||||
|
||||
If this style of interpreter was discussed back in the 70's, why hasn't it been more popular? I suspect because there was no benefit until more recently. Older calling conventions only used the stack to pass arguments, older CPUs didn't have branch prediction and compiler tail-call optimization maybe weren't ubiqutous.
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
# Performance
|
||||
|
||||
## CoreMark 1.0 results
|
||||
|
||||
```log
|
||||
# Intel i5-8250U x64 (1.6-3.4GHz)
|
||||
|
||||
Node v13.0.1 (interpreter) 28.544923 57.0x
|
||||
wac (wax) 101.864113 16.0x
|
||||
wasm-micro-runtime 201.612903 8.1x ▲ slower
|
||||
wasm3 1627.869119 1.0
|
||||
Wasmer 0.13.1 singlepass 4065.591544 2.5x ▼ faster
|
||||
wasmtime 0.8.0 (--optimize) 6453.505427 4.0x
|
||||
Wasmer 0.13.1 cranelift 6467.164442 4.0x
|
||||
Webassembly.sh (Chromium 78) 6914.325225 4.2x
|
||||
Webassembly.sh (Firefox 70) 7251.153593 4.5x
|
||||
wasmer-js (Node v13.0.1) 10043.827611 6.2x
|
||||
Wasmer 0.13.1 llvm 12450.199203 7.6x
|
||||
WAVM 14946.566026 9.2x
|
||||
Native (GCC 7.4.0, 32-bit) 18070.112035 11.1x
|
||||
Native (GCC 7.4.0, 64-bit) 19144.862795 11.8x
|
||||
```
|
||||
|
||||
**Note:** Here is more info on [how to run CoreMark benchmark](https://github.com/wasm3/wasm-coremark).
|
||||
|
||||
## Simple recursive Fibonacci calculation test
|
||||
|
||||
```log
|
||||
fib(40)
|
||||
-----------------------------------------------------------------------------------------
|
||||
# Intel i5-8250U x64 (1.6-3.4GHz)
|
||||
Native C implementation 0.23s
|
||||
Linux 3.83s
|
||||
Win 10 5.35s
|
||||
wasm3 on V8 (Emscripten 1.38, node v13.0.1) 17.98s
|
||||
|
||||
# Raspberry Pi 4 BCM2711B0 armv7l (A72 @ 1.5GHz)
|
||||
Native C implementation 1.11s
|
||||
Linux 22.97s
|
||||
|
||||
# Orange Pi Zero Plus2 H5 aarch64 (A53 @ 1GHz)
|
||||
Native C implementation 2.55s
|
||||
Linux 50.00s
|
||||
|
||||
# VoCore2 mips32r2 (MT7628AN @ 580MHz)
|
||||
Native C implementation 6.21s
|
||||
OpenWRT 2m 38s
|
||||
|
||||
# Xiaomi Mi Router 3G mips32r2 (MT7621AT @ 880MHz)
|
||||
Native C implementation 8.83s
|
||||
OpenWRT 3m 20s
|
||||
```
|
||||
|
||||
## Wasm3 on MCUs
|
||||
|
||||
```log
|
||||
fib(24) comments
|
||||
-----------------------------------------------------------------------------------------
|
||||
Maix (K210) rv64imafc @ 400MHz 77ms
|
||||
ESP8266 LX106 @ 160MHz 308ms TCO failed, stack used: 9024
|
||||
ESP32 LX6 @ 240MHz 297ms TCO failed, stack used: 10600
|
||||
ESP32-s2 (beta) LX6 @ 240MHz 297ms TCO failed
|
||||
Particle Photon Arm M3 @ 120MHz 536ms
|
||||
MXChip AZ3166 Arm M4 @ 100MHz ms
|
||||
WM W600 Arm M3 @ 80MHz 698ms TCO enabled, stack used: 1325
|
||||
WM W600 Arm M3 @ 80MHz 826ms TCO disabled, stack used: 8109
|
||||
Arduino DUE (SAM3X8E) Arm M3 @ 84MHz 754ms
|
||||
BleNano2 (nRF52) Arm M4 @ 64MHz 1.19s
|
||||
Arduino MKR1000 Arm M0+ @ 48MHz 1.93s TCO failed
|
||||
TinyBLE (nRF51) Arm M0 @ 16MHz 5.69s TCO failed
|
||||
BluePill Arm M3 @ 72MHz 7.62s
|
||||
HiFive1 (FE310) rv32imac @ 320MHz 9.10s <- something wrong here?
|
||||
ATmega1284 avr @ 16MHz 12.99s TCO failed
|
||||
Fomu rv32i @ 12MHz 25.20s
|
||||
```
|
||||
|
||||
## Wasm3 vs other languages
|
||||
|
||||
```log
|
||||
fib(40)
|
||||
-----------------------------------------------------------------------------------------
|
||||
LuaJIT jit 1.15s
|
||||
Node v10.15 jit 2.97s ▲ faster
|
||||
Wasm3 interp 3.83s
|
||||
Lua 5.1 interp 16.65s ▼ slower
|
||||
Python 2.7 interp 34.08s
|
||||
Python 3.4 interp 35.67s
|
||||
Micropython v1.11 interp 85,00s
|
||||
Espruino 2v04 interp >20m
|
||||
```
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
# Wasm3 tests
|
||||
|
||||
## Running WebAssembly spec tests
|
||||
|
||||
To run spec tests, you need `python3`
|
||||
|
||||
```sh
|
||||
# In test directory:
|
||||
python3 ./run-spec-test.py
|
||||
```
|
||||
|
||||
It will automatically download, extract, run the WebAssembly core test suite.
|
||||
|
||||
## Running WASI test
|
||||
|
||||
Wasm3 comes with a set of benchmarks and test programs (prebuilt as `WASI` apps) including `CoreMark`, `C-Ray`, `Brotli`, `mandelbrot`, `smallpt` and `wasm3` itself.
|
||||
|
||||
This test will run all of them and verify the output:
|
||||
|
||||
```sh
|
||||
# In test directory:
|
||||
python3 ./run-wasi-test.py
|
||||
```
|
||||
|
||||
It can be run against other engines as well:
|
||||
|
||||
```sh
|
||||
./run-wasi-test.py --exec wasmtime # [PASS]
|
||||
./run-wasi-test.py --exec "wavm run" # [PASS]
|
||||
./run-wasi-test.py --exec "wasmer run" # [PASS]
|
||||
./run-wasi-test.py --exec "wasmer-js run" # [PASS]
|
||||
./run-wasi-test.py --exec $WAMR/iwasm --timeout=300 # [PASS, but very slow]
|
||||
./run-wasi-test.py --exec $WAC/wax --timeout=300 # [FAIL, crashes on most tests]
|
||||
```
|
||||
|
||||
## Running coverage-guided fuzz testing with libFuzzer
|
||||
|
||||
You need to produce a fuzzer build first (use your version of Clang):
|
||||
|
||||
```sh
|
||||
# In wasm3 root:
|
||||
mkdir build-fuzzer
|
||||
cd build-fuzzer
|
||||
cmake -GNinja -DCLANG_SUFFIX="-9" ..
|
||||
cmake -DBUILD_FUZZ:BOOL=TRUE ..
|
||||
ninja
|
||||
```
|
||||
|
||||
```sh
|
||||
# In test directory:
|
||||
../build-fuzzer/wasm3-fuzzer -detect_leaks=0 ./fuzz/corpus
|
||||
```
|
||||
|
||||
Read [more on libFuzzer](https://llvm.org/docs/LibFuzzer.html) and it's options.
|
||||
|
||||
Note: to catch fuzzer errors in debugger, you need to define:
|
||||
```sh
|
||||
export ASAN_OPTIONS=abort_on_error=1
|
||||
export UBSAN_OPTIONS=abort_on_error=1
|
||||
```
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
# Wasm3 troubleshooting
|
||||
|
||||
### `Error: [trap] stack overflow`
|
||||
|
||||
Increase the stack size:
|
||||
```sh
|
||||
wasm3 --stack-size 1000000 file.wasm
|
||||
```
|
||||
|
||||
### `Error: missing imported function`
|
||||
|
||||
This means that the runtime doesn't provide a specific function, needed for your module execution.
|
||||
You need to implement the required functions, and re-build Wasm3.
|
||||
Alternatively, you can use Python to define your environment. Check out [`pywasm3`](https://pypi.org/project/pywasm3/) module.
|
||||
|
||||
**Note:** If this happens with `WASI` functions like `wasi_unstable.*` or `wasi_snapshot_preview1.*`, please report as a bug.
|
||||
|
||||
### `Error: compiling function overran its stack height limit`
|
||||
|
||||
Try increasing `d_m3MaxFunctionStackHeight` in `m3_config.h` and rebuilding Wasm3.
|
||||
|
||||
### `Error: [Fatal] repl_load: unallocated linear memory`
|
||||
|
||||
Your module requires some `Memory`, but doesn't define/export it by itself.
|
||||
This happens if module is built by `Emscripten`, or it's a library that is intended to be linked to some other modules.
|
||||
Wasm3 currently doesn't support running such modules directly, but you can remove this limitation when embedding Wasm3 into your own app.
|
After Width: | Height: | Size: 8.5 KiB |
After Width: | Height: | Size: 3.9 KiB |
|
@ -0,0 +1,17 @@
|
|||
#
|
||||
# Utility to disassemble a specific function.
|
||||
# Usage:
|
||||
# ./disasm-func.sh ../build/wasm3 i32_Divide_sr
|
||||
#
|
||||
|
||||
FILE="$1"
|
||||
FUNC="$2"
|
||||
|
||||
# get starting address and size of function fact from nm
|
||||
ADDR=$(nm --print-size --radix=d $FILE | grep $FUNC | cut -d ' ' -f 1,2)
|
||||
# strip leading 0's to avoid being interpreted by objdump as octal addresses
|
||||
STARTADDR=$(echo $ADDR | cut -d ' ' -f 1 | sed 's/^0*\(.\)/\1/')
|
||||
SIZE=$(echo $ADDR | cut -d ' ' -f 2 | sed 's/^0*//')
|
||||
STOPADDR=$(( $STARTADDR + $SIZE ))
|
||||
|
||||
objdump --disassemble $FILE --start-address=$STARTADDR --stop-address=$STOPADDR -M suffix
|
|
@ -0,0 +1,202 @@
|
|||
<svg version="1.1" id="grid" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 214 77" style="enable-background:new 0 0 214 77;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#674BDE;}
|
||||
.st1{fill:#4D38A5;}
|
||||
.st2{fill:#0799AC;}
|
||||
.st3{fill:#187C80;}
|
||||
.st4{fill:#09CDE7;}
|
||||
.st5{filter:url(#Adobe_OpacityMaskFilter);}
|
||||
.st6{fill:url(#SVGID_2_);}
|
||||
.st7{opacity:0.23;mask:url(#SVGID_1_);fill:url(#SVGID_3_);}
|
||||
.st8{fill:#C9F1F6;}
|
||||
.st9{fill:#DCD6F4;}
|
||||
.st10{clip-path:url(#SVGID_7_);fill:url(#SVGID_8_);}
|
||||
.st11{filter:url(#Adobe_OpacityMaskFilter_1_);}
|
||||
.st12{clip-path:url(#SVGID_7_);fill:url(#SVGID_10_);stroke:#1D1D1B;stroke-width:0.9757;stroke-miterlimit:10;}
|
||||
.st13{clip-path:url(#SVGID_7_);mask:url(#SVGID_9_);fill:url(#SVGID_11_);}
|
||||
.st14{opacity:0.5;fill:#7F6A55;}
|
||||
.st15{fill:#1D1D1B;}
|
||||
.st16{fill:#7D6852;}
|
||||
.st17{fill:#E03D3D;}
|
||||
.st18{clip-path:url(#SVGID_13_);}
|
||||
.st19{fill:none;}
|
||||
.st20{fill:none;stroke:#09CDE7;stroke-width:20.4194;stroke-linecap:round;stroke-miterlimit:10;}
|
||||
.st21{fill:none;stroke:#674BDE;stroke-width:20.4194;stroke-linecap:round;stroke-miterlimit:10;}
|
||||
.st22{fill:#98E8F2;}
|
||||
.st23{fill:#FFFFFF;}
|
||||
.st24{fill:#3D3196;}
|
||||
.st25{opacity:0.1;fill:#1D1D1B;}
|
||||
.st26{opacity:0.1;}
|
||||
.st27{fill:#373735;}
|
||||
.st28{fill:none;stroke:#674BDE;stroke-width:1.0781;stroke-linecap:round;stroke-miterlimit:10;}
|
||||
.st29{fill:none;stroke:#674BDE;stroke-width:1.2938;stroke-linecap:round;stroke-miterlimit:10;}
|
||||
.st30{fill:none;stroke:#674BDE;stroke-width:1.0707;stroke-linecap:round;stroke-miterlimit:10;}
|
||||
.st31{opacity:0.2;fill:#1D1D1B;}
|
||||
.st32{fill:#806FE4;}
|
||||
.st33{fill:none;stroke:#FFFFFF;stroke-width:6.4688;stroke-linecap:round;stroke-miterlimit:10;}
|
||||
.st34{opacity:0.2;}
|
||||
.st35{fill:#057780;}
|
||||
.st36{fill:none;stroke:#9F8EE9;stroke-width:2.3621;stroke-linecap:round;stroke-miterlimit:10;}
|
||||
.st37{fill:none;stroke:#9F8EE9;stroke-width:2.3621;stroke-linecap:round;stroke-miterlimit:10;stroke-dasharray:7.0935,11.8225;}
|
||||
.st38{fill:#342672;}
|
||||
.st39{fill:#1A1339;}
|
||||
.st40{fill:#6BDFEF;}
|
||||
.st41{fill:none;stroke:#674BDE;stroke-width:2.3883;stroke-linecap:round;stroke-miterlimit:10;}
|
||||
.st42{fill:#3A2A80;}
|
||||
.st43{fill:url(#SVGID_14_);}
|
||||
.st44{fill:url(#SVGID_15_);}
|
||||
.st45{fill:url(#SVGID_16_);}
|
||||
.st46{fill:#F0F0F0;}
|
||||
.st47{opacity:0.5;}
|
||||
.st48{opacity:0.25;fill:#1A1339;}
|
||||
.st49{clip-path:url(#SVGID_18_);}
|
||||
.st50{fill:none;stroke:#FFFFFF;stroke-width:1.1495;stroke-linecap:round;stroke-miterlimit:10;}
|
||||
.st51{fill:#056976;}
|
||||
.st52{fill:#B1A4ED;}
|
||||
.st53{fill:#3AD6EB;}
|
||||
.st54{fill:none;stroke:#9F8EE9;stroke-width:1.7304;stroke-linecap:round;stroke-miterlimit:10;stroke-dasharray:5.1912,8.6519;}
|
||||
.st55{fill:url(#SVGID_19_);}
|
||||
.st56{opacity:0.5;fill:#FFFFFF;}
|
||||
.st57{fill:url(#SVGID_22_);}
|
||||
.st58{fill:url(#SVGID_23_);}
|
||||
.st59{fill:none;stroke:#674BDE;stroke-miterlimit:10;}
|
||||
.st60{fill:url(#SVGID_24_);}
|
||||
.st61{fill:url(#SVGID_29_);}
|
||||
.st62{fill:url(#SVGID_30_);}
|
||||
.st63{fill:url(#SVGID_33_);}
|
||||
.st64{fill:url(#SVGID_34_);}
|
||||
.st65{fill:url(#SVGID_35_);}
|
||||
.st66{fill:url(#SVGID_42_);}
|
||||
.st67{filter:url(#Adobe_OpacityMaskFilter_2_);}
|
||||
.st68{fill:url(#SVGID_44_);stroke:#1D1D1B;stroke-width:0.9757;stroke-miterlimit:10;}
|
||||
.st69{mask:url(#SVGID_43_);fill:url(#SVGID_45_);}
|
||||
.st70{fill:url(#SVGID_46_);}
|
||||
.st71{fill:url(#SVGID_47_);}
|
||||
.st72{clip-path:url(#SVGID_49_);}
|
||||
.st73{fill:none;stroke:#674BDE;stroke-width:3;stroke-miterlimit:10;}
|
||||
.st74{fill:none;stroke:#674BDE;stroke-width:0.8089;stroke-linecap:round;stroke-miterlimit:10;}
|
||||
.st75{fill:none;stroke:#674BDE;stroke-width:0.9707;stroke-linecap:round;stroke-miterlimit:10;}
|
||||
.st76{fill:none;stroke:#674BDE;stroke-width:0.8033;stroke-linecap:round;stroke-miterlimit:10;}
|
||||
.st77{fill:none;stroke:#FFFFFF;stroke-width:4.8536;stroke-linecap:round;stroke-miterlimit:10;}
|
||||
.st78{fill:none;stroke:#9F8EE9;stroke-width:1.7184;stroke-linecap:round;stroke-miterlimit:10;}
|
||||
.st79{fill:none;stroke:#9F8EE9;stroke-width:1.7184;stroke-linecap:round;stroke-miterlimit:10;stroke-dasharray:5.1601,8.6002;}
|
||||
.st80{fill:none;stroke:#674BDE;stroke-width:1.7374;stroke-linecap:round;stroke-miterlimit:10;}
|
||||
.st81{fill:none;stroke:#BDB2EF;stroke-miterlimit:10;}
|
||||
.st82{fill:none;stroke:#9F8EE9;stroke-width:1.2056;stroke-linecap:round;stroke-miterlimit:10;stroke-dasharray:3.6169,6.0282;}
|
||||
.st83{fill:url(#SVGID_50_);}
|
||||
.st84{fill:url(#SVGID_51_);}
|
||||
.st85{clip-path:url(#SVGID_53_);}
|
||||
.st86{fill:none;stroke:#FFFFFF;stroke-width:3;stroke-miterlimit:10;}
|
||||
.st87{fill:#E3DEF6;}
|
||||
.st88{fill:url(#SVGID_54_);}
|
||||
.st89{opacity:0.56;fill:url(#SVGID_55_);}
|
||||
.st90{opacity:0.56;fill:url(#SVGID_56_);}
|
||||
.st91{opacity:0.56;fill:url(#SVGID_57_);}
|
||||
.st92{opacity:0.56;fill:url(#SVGID_64_);}
|
||||
.st93{opacity:0.56;fill:url(#SVGID_65_);}
|
||||
.st94{opacity:0.56;fill:url(#SVGID_66_);}
|
||||
.st95{fill:#E7E7E7;}
|
||||
.st96{fill:url(#SVGID_67_);}
|
||||
.st97{fill:url(#SVGID_68_);}
|
||||
.st98{fill:none;stroke:#FFFFFF;stroke-miterlimit:10;}
|
||||
.st99{fill:none;stroke:#C1C1C0;stroke-miterlimit:10;}
|
||||
.st100{fill:none;stroke:#09CDE7;stroke-miterlimit:10;}
|
||||
.st101{fill:url(#SVGID_85_);}
|
||||
.st102{clip-path:url(#SVGID_101_);}
|
||||
.st103{fill:url(#SVGID_102_);}
|
||||
.st104{filter:url(#Adobe_OpacityMaskFilter_3_);}
|
||||
.st105{fill:url(#SVGID_104_);stroke:#1D1D1B;stroke-width:0.5657;stroke-miterlimit:10;}
|
||||
.st106{mask:url(#SVGID_103_);fill:url(#SVGID_105_);}
|
||||
.st107{fill:url(#SVGID_106_);}
|
||||
.st108{fill:url(#SVGID_107_);}
|
||||
.st109{clip-path:url(#SVGID_109_);}
|
||||
.st110{fill:none;stroke:#FFFFFF;stroke-width:0.6665;stroke-linecap:round;stroke-miterlimit:10;}
|
||||
.st111{fill:url(#SVGID_110_);}
|
||||
.st112{clip-path:url(#SVGID_126_);}
|
||||
.st113{fill:none;stroke:#674BDE;stroke-width:0.469;stroke-linecap:round;stroke-miterlimit:10;}
|
||||
.st114{fill:none;stroke:#674BDE;stroke-width:0.5628;stroke-linecap:round;stroke-miterlimit:10;}
|
||||
.st115{fill:none;stroke:#674BDE;stroke-width:0.4658;stroke-linecap:round;stroke-miterlimit:10;}
|
||||
.st116{fill:none;stroke:#FFFFFF;stroke-width:2.8141;stroke-linecap:round;stroke-miterlimit:10;}
|
||||
.st117{fill:none;stroke:#BDB2EF;stroke-width:0.5798;stroke-miterlimit:10;}
|
||||
.st118{fill:none;stroke:#9F8EE9;stroke-width:0.699;stroke-linecap:round;stroke-miterlimit:10;stroke-dasharray:2.0971,3.4951;}
|
||||
.st119{clip-path:url(#SVGID_142_);}
|
||||
.st120{fill:url(#SVGID_143_);}
|
||||
.st121{fill:url(#SVGID_144_);}
|
||||
.st122{clip-path:url(#SVGID_146_);}
|
||||
.st123{fill:none;stroke:#FFFFFF;stroke-width:1.7394;stroke-miterlimit:10;}
|
||||
.st124{fill:#D5D6D9;}
|
||||
.st125{fill:#F7F7F7;}
|
||||
.st126{fill:url(#SVGID_147_);}
|
||||
.st127{fill:#BCBCBC;}
|
||||
.st128{fill:url(#SVGID_148_);}
|
||||
.st129{fill:#3C3E42;}
|
||||
.st130{filter:url(#Adobe_OpacityMaskFilter_4_);}
|
||||
.st131{clip-path:url(#SVGID_150_);fill:url(#SVGID_152_);stroke:#1D1D1B;stroke-width:0.4331;stroke-miterlimit:10;}
|
||||
.st132{clip-path:url(#SVGID_150_);mask:url(#SVGID_151_);fill:url(#SVGID_153_);}
|
||||
.st133{fill:url(#SVGID_154_);}
|
||||
.st134{fill:url(#SVGID_155_);}
|
||||
.st135{fill:url(#SVGID_156_);}
|
||||
.st136{clip-path:url(#SVGID_158_);}
|
||||
.st137{fill:none;stroke:#FFFFFF;stroke-width:0.3836;stroke-linecap:round;stroke-miterlimit:10;}
|
||||
.st138{fill:url(#SVGID_159_);}
|
||||
.st139{fill:none;stroke:#674BDE;stroke-width:0.3711;stroke-linecap:round;stroke-miterlimit:10;}
|
||||
.st140{fill:none;stroke:#674BDE;stroke-width:0.4454;stroke-linecap:round;stroke-miterlimit:10;}
|
||||
.st141{fill:none;stroke:#674BDE;stroke-width:0.3686;stroke-linecap:round;stroke-miterlimit:10;}
|
||||
.st142{fill:none;stroke:#FFFFFF;stroke-width:2.2268;stroke-linecap:round;stroke-miterlimit:10;}
|
||||
.st143{fill:none;stroke:#BDB2EF;stroke-width:0.4715;stroke-miterlimit:10;}
|
||||
.st144{clip-path:url(#SVGID_161_);}
|
||||
.st145{fill:none;stroke:#9F8EE9;stroke-width:0.5276;stroke-linecap:round;stroke-miterlimit:10;stroke-dasharray:1.5829,2.6381;}
|
||||
.st146{fill:url(#SVGID_162_);}
|
||||
.st147{fill:none;stroke:#FFFFFF;stroke-width:1.6174;stroke-miterlimit:10;}
|
||||
.st148{clip-path:url(#SVGID_164_);}
|
||||
.st149{fill:#382A87;}
|
||||
.st150{fill:#0A5454;}
|
||||
.st151{fill:url(#SVGID_165_);}
|
||||
</style>
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st15" d="M14.9,18.3c-0.5-0.5-0.8-1.1-0.8-1.8c0-0.7,0.3-1.2,0.8-1.7c0.5-0.5,1.2-0.7,1.9-0.7
|
||||
c0.8,0,1.4,0.2,1.9,0.7c0.5,0.5,0.8,1,0.8,1.7c0,0.7-0.3,1.3-0.8,1.8c-0.5,0.5-1.1,0.8-1.9,0.8C16.1,19.1,15.5,18.8,14.9,18.3z
|
||||
M15.7,65.2V28.4c0-0.4,0.2-0.6,0.6-0.6h1.1c0.4,0,0.6,0.2,0.6,0.6v36.8c0,0.4-0.2,0.6-0.6,0.6h-1.1
|
||||
C15.9,65.8,15.7,65.6,15.7,65.2z"></path>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st15" d="M57.3,12.7h1.1c0.4,0,0.6,0.2,0.6,0.6v51.9c0,0.4-0.2,0.6-0.6,0.6h-1.1c-0.4,0-0.6-0.2-0.6-0.6v-4.9
|
||||
c0-0.1,0-0.2-0.1-0.2c-0.1,0-0.1,0-0.2,0.1c-1.1,1.9-2.6,3.4-4.5,4.5c-1.9,1.1-4.1,1.6-6.6,1.6c-3.4,0-6.2-0.9-8.6-2.7
|
||||
c-2.3-1.8-3.9-4.2-4.6-7.3c-0.3-1.1-0.4-2.3-0.5-3.6c-0.1-1.3-0.1-3.3-0.1-5.8c0-2.6,0-4.6,0.1-6c0.1-1.4,0.2-2.6,0.5-3.7
|
||||
c0.7-3,2.2-5.4,4.6-7.2c2.4-1.8,5.3-2.8,8.6-2.8c2.5,0,4.7,0.5,6.6,1.6c1.9,1.1,3.4,2.6,4.5,4.6c0,0.1,0.1,0.1,0.2,0.1
|
||||
c0,0,0.1-0.1,0.1-0.2v-20C56.7,12.9,56.9,12.7,57.3,12.7z M56.6,52.8c0.1-1,0.1-3,0.1-6c0-2.9,0-4.9-0.1-6c-0.1-1-0.2-2-0.4-2.9
|
||||
c-0.5-2.5-1.6-4.5-3.5-6.2c-1.9-1.6-4.2-2.5-7.1-2.5c-2.9,0-5.3,0.8-7.4,2.5c-2,1.6-3.3,3.7-3.8,6.2c-0.2,0.9-0.3,1.8-0.4,2.8
|
||||
c-0.1,1-0.1,3-0.1,6c0,2.9,0,4.8,0.1,5.9c0.1,1,0.2,2,0.3,3c0.4,2.5,1.6,4.6,3.7,6.2c2.1,1.6,4.6,2.4,7.5,2.4
|
||||
c2.9,0,5.2-0.8,7.1-2.5c1.8-1.6,3-3.7,3.5-6.2C56.4,54.8,56.5,53.8,56.6,52.8z"></path>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st15" d="M93.4,62.4c2-1.3,3.4-3,4.2-5.1l0.2-0.3c0.1-0.2,0.2-0.2,0.4-0.2h0.4l0.8,0.2c0.4,0.1,0.5,0.4,0.4,0.8
|
||||
c-1,2.6-2.6,4.7-5,6.3c-2.4,1.6-5.1,2.4-8.4,2.4c-3.4,0-6.3-0.9-8.7-2.7c-2.4-1.8-3.9-4.2-4.7-7.2c-0.3-1.2-0.5-2.4-0.6-3.8
|
||||
c-0.1-1.4-0.1-3.4-0.1-6c0-2.4,0-4.3,0.1-5.7c0.1-1.4,0.2-2.6,0.5-3.8c0.7-3,2.3-5.5,4.7-7.3c2.4-1.8,5.3-2.8,8.8-2.8
|
||||
c3.6,0,6.7,0.9,9.1,2.8c2.4,1.9,4,4.4,4.6,7.6c0.2,0.9,0.3,2.1,0.4,3.5c0,1.4,0.1,3.2,0.1,5.6c0,0.4-0.2,0.6-0.6,0.6H74.7
|
||||
c-0.2,0-0.2,0.1-0.2,0.2c0,1.8,0,3.3,0.1,4.7c0,1.4,0.2,2.4,0.3,3.1c0.5,2.7,1.8,4.8,3.8,6.5c2,1.6,4.6,2.5,7.7,2.5
|
||||
C89.1,64.3,91.4,63.7,93.4,62.4z M78.6,31.8c-2,1.6-3.3,3.8-3.8,6.4c-0.3,1.2-0.4,3.5-0.4,6.9c0,0.2,0.1,0.2,0.2,0.2h23.3
|
||||
c0.2,0,0.2-0.1,0.2-0.2c0-3.4-0.1-5.7-0.4-6.8c-0.6-2.7-1.9-4.8-3.9-6.5c-2-1.6-4.6-2.5-7.6-2.5C83.2,29.3,80.6,30.2,78.6,31.8z
|
||||
"></path>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st15" d="M136.5,30.8c2.4,2.4,3.6,5.4,3.6,9.2v25.2c0,0.4-0.2,0.6-0.6,0.6h-1.1c-0.4,0-0.6-0.2-0.6-0.6V40.4
|
||||
c0-3.2-1-5.9-3-7.9c-2-2-4.6-3.1-7.8-3.1c-3.4,0-6.1,1-8.2,3c-2.1,2-3.1,4.6-3.1,7.8v25c0,0.4-0.2,0.6-0.6,0.6H114
|
||||
c-0.4,0-0.6-0.2-0.6-0.6V28.4c0-0.4,0.2-0.6,0.6-0.6h1.1c0.4,0,0.6,0.2,0.6,0.6V33c0,0.1,0,0.2,0.1,0.2c0,0.1,0.1,0,0.2-0.1
|
||||
c1.1-1.9,2.6-3.3,4.6-4.3c1.9-1,4.2-1.5,6.8-1.5C131.1,27.3,134.2,28.5,136.5,30.8z"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="194.2343" y1="58.916" x2="157.4035" y2="11.5682">
|
||||
<stop offset="0" style="stop-color:#09CDE7"></stop>
|
||||
<stop offset="1" style="stop-color:#674BDE"></stop>
|
||||
</linearGradient>
|
||||
<path style="fill:url(#SVGID_1_);" d="M177,69.5c-12.6,0-22.8-10.2-22.8-22.8c0-3.2,2.6-5.8,5.8-5.8c3.2,0,5.8,2.6,5.8,5.8
|
||||
c0,6.2,5.1,11.3,11.3,11.3s11.3-5,11.3-11.3c0-4.6-2.8-8.7-7-10.4c-3-1.2-4.4-4.6-3.2-7.5c1.2-3,4.6-4.4,7.5-3.2
|
||||
c8.6,3.5,14.2,11.8,14.2,21.1C199.9,59.3,189.6,69.5,177,69.5z M193.8,13.3c0-3.2-2.6-5.8-5.8-5.8h-28c-3.2,0-5.8,2.6-5.8,5.8
|
||||
s2.6,5.8,5.8,5.8h28C191.2,19,193.8,16.4,193.8,13.3z"></path>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 8.5 KiB |
After Width: | Height: | Size: 51 KiB |
After Width: | Height: | Size: 86 KiB |
After Width: | Height: | Size: 10 KiB |
|
@ -0,0 +1,57 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Author: Volodymyr Shymanskyy
|
||||
|
||||
import os
|
||||
import re, fnmatch
|
||||
import pathlib
|
||||
|
||||
class ansi:
|
||||
ENDC = '\033[0m'
|
||||
HEADER = '\033[94m'
|
||||
OKGREEN = '\033[92m'
|
||||
WARNING = '\033[93m'
|
||||
FAIL = '\033[91m'
|
||||
BOLD = '\033[1m'
|
||||
UNDERLINE = '\033[4m'
|
||||
|
||||
class dotdict(dict):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(dotdict, self).__init__(*args, **kwargs)
|
||||
for arg in args:
|
||||
if isinstance(arg, dict):
|
||||
for k, v in arg.items():
|
||||
self[k] = v
|
||||
if kwargs:
|
||||
for k, v in kwargs.items():
|
||||
self[k] = v
|
||||
|
||||
__getattr__ = dict.get
|
||||
__setattr__ = dict.__setitem__
|
||||
__delattr__ = dict.__delitem__
|
||||
|
||||
class Blacklist():
|
||||
def __init__(self, patterns):
|
||||
self._patterns = list(map(fnmatch.translate, patterns))
|
||||
self.update()
|
||||
|
||||
def add(self, patterns):
|
||||
self._patterns += list(map(fnmatch.translate, patterns))
|
||||
self.update()
|
||||
|
||||
def update(self):
|
||||
self._regex = re.compile('|'.join(self._patterns))
|
||||
|
||||
def __contains__(self, item):
|
||||
return self._regex.match(item) is not None
|
||||
|
||||
def filename(p):
|
||||
_, fn = os.path.split(p)
|
||||
return fn
|
||||
|
||||
def pathname(p):
|
||||
pn, _ = os.path.split(p)
|
||||
return pn
|
||||
|
||||
def ensure_path(p):
|
||||
pathlib.Path(p).mkdir(parents=True, exist_ok=True)
|
|
@ -0,0 +1,39 @@
|
|||
.PHONY: format
|
||||
|
||||
all:
|
||||
|
||||
format:
|
||||
# Convert tabs to spaces
|
||||
find ./source -type f -iname '*.[cpp\|c\|h]' -exec bash -c 'expand -t 4 "$$0" | sponge "$$0"' {} \;
|
||||
# Remove trailing whitespaces
|
||||
find ./source -type f -iname '*.[cpp\|c\|h]' -exec sed -i 's/ *$$//' {} \;
|
||||
|
||||
preprocess: preprocess_restore
|
||||
cp ./source/m3_exec.h ./source/m3_exec.h.bak
|
||||
cp ./source/m3_compile.c ./source/m3_compile.c.bak
|
||||
awk 'BEGIN {RS=""}{gsub(/\\\n/,"\\\n__NL__ ")}1' ./source/m3_exec.h | sponge ./source/m3_exec.h
|
||||
cpp -I./source -P ./source/m3_compile.c | sponge ./source/m3_compile.c
|
||||
awk '{gsub(/__NL__/,"\n")}1' ./source/m3_compile.c | sponge ./source/m3_compile.c
|
||||
mv ./source/m3_exec.h.bak ./source/m3_exec.h
|
||||
|
||||
preprocess_restore:
|
||||
-mv source/m3_compile.c.bak source/m3_compile.c
|
||||
touch source/m3_compile.c
|
||||
|
||||
|
||||
test_rv64:
|
||||
riscv64-linux-gnu-gcc -DDEBUG -Dd_m3HasWASI \
|
||||
-I./source ./source/*.c ./platforms/app/main.c \
|
||||
-O3 -g0 -flto -lm -static \
|
||||
-o wasm3-rv64
|
||||
|
||||
cd test; python3 run-wasi-test.py --fast --exec "qemu-riscv64-static ../wasm3-rv64"
|
||||
rm ./wasm3-rv64
|
||||
|
||||
test_cpp:
|
||||
clang -xc++ -Dd_m3HasWASI \
|
||||
-I./source ./source/*.c ./platforms/app/main.c \
|
||||
-O3 -g0 -flto -lm -static \
|
||||
-o wasm3-cpp
|
||||
cd test; python3 run-wasi-test.py --fast --exec "../wasm3-cpp"
|
||||
rm ./wasm3-cpp
|
|
@ -0,0 +1,105 @@
|
|||
# Wasm3
|
||||
|
||||
[Wasm3](https://github.com/wasm3/wasm3) is the fastest WebAssembly interpreter, and the most universal runtime.
|
||||
It's packaged into a `WebAssembly` package, so you can finally run `WebAssembly` on `WebAssembly` 😆
|
||||
|
||||
## Running on WebAssembly.sh
|
||||
|
||||
Open [**WebAssembly.sh**](https://webassembly.sh)
|
||||
|
||||
First you need to fetch a wasm file you'd like to run:
|
||||
|
||||
```sh
|
||||
$ curl https://raw.githubusercontent.com/wasm3/wasm3/main/test/lang/fib32.wasm -o /tmp/fib32.wasm
|
||||
|
||||
$ ls -l /tmp/fib32.wasm
|
||||
---------- 1 somebody somegroup 62 1970-01-19 05:45 /tmp/fib32.wasm
|
||||
```
|
||||
|
||||
Now we can run `wasm3` in interactive mode:
|
||||
|
||||
```sh
|
||||
$ wasm3 --repl /tmp/fib32.wasm
|
||||
wasm3> fib 20
|
||||
Result: 6765
|
||||
wasm3> fib 30
|
||||
Result: 832040
|
||||
wasm3> ^C
|
||||
$
|
||||
```
|
||||
|
||||
Or run a specific function directly:
|
||||
|
||||
```sh
|
||||
$ wasm3 --func fib /tmp/fib32.wasm 30
|
||||
Result: 832040
|
||||
```
|
||||
|
||||
`wasm3` also supports `WASI`, so you can run:
|
||||
|
||||
```sh
|
||||
$ curl https://raw.githubusercontent.com/wasm3/wasm3/main/test/wasi/simple/test.wasm -o /tmp/test.wasm
|
||||
$ wasm3 /tmp/test.wasm
|
||||
```
|
||||
|
||||
or even... run wasm3 inside wasm3:
|
||||
|
||||
```sh
|
||||
$ curl https://registry-cdn.wapm.io/contents/vshymanskyy/wasm3/0.5.0/build/wasm3-wasi.wasm -o /tmp/wasm3.wasm
|
||||
$ wasm3 --stack-size 100000 /tmp/wasm3.wasm /tmp/test.wasm
|
||||
```
|
||||
|
||||
## Tracing
|
||||
|
||||
You can also get structured traces of arbitrary WASM file execution (and this requires no specific support from the runtime):
|
||||
|
||||
```sh
|
||||
$ wasm3-strace --repl /tmp/fib32.wasm
|
||||
wasm3> fib 3
|
||||
fib (i32: 3) {
|
||||
fib (i32: 1) {
|
||||
} = 1
|
||||
fib (i32: 2) {
|
||||
fib (i32: 0) {
|
||||
} = 0
|
||||
fib (i32: 1) {
|
||||
} = 1
|
||||
} = 1
|
||||
} = 2
|
||||
Result: 2
|
||||
```
|
||||
|
||||
WASI apps tracing is also supported:
|
||||
|
||||
```sh
|
||||
$ wasm3-strace /tmp/test.wasm trap
|
||||
_start () {
|
||||
__wasm_call_ctors () {
|
||||
__wasilibc_populate_preopens () {
|
||||
wasi_snapshot_preview1!fd_prestat_get(3, 65528) { <native> } = 0
|
||||
malloc (i32: 2) {
|
||||
dlmalloc (i32: 2) {
|
||||
sbrk (i32: 0) {
|
||||
} = 131072
|
||||
} = 70016
|
||||
} = 70016
|
||||
...
|
||||
strcmp (i32: 70127, i32: 32) {
|
||||
} = 0
|
||||
test_trap () {
|
||||
a () {
|
||||
b () {
|
||||
c () {
|
||||
} !trap = [trap] unreachable executed
|
||||
...
|
||||
==== wasm backtrace:
|
||||
0: 0x000c59 - .unnamed!c
|
||||
1: 0x000c5e - .unnamed!b
|
||||
2: 0x000c68 - .unnamed!a
|
||||
3: 0x000c72 - .unnamed!test_trap
|
||||
4: 0x000d2c - .unnamed!main
|
||||
5: 0x0037c9 - .unnamed!__main_void
|
||||
6: 0x000edb - .unnamed!__original_main
|
||||
7: 0x0002f3 - .unnamed!_start
|
||||
Error: [trap] unreachable executed
|
||||
```
|
|
@ -0,0 +1,4 @@
|
|||
# Ignore everything in this directory
|
||||
*
|
||||
# Except this file
|
||||
!.gitignore
|
|
@ -0,0 +1,25 @@
|
|||
[package]
|
||||
name = "vshymanskyy/wasm3"
|
||||
version = "0.5.0"
|
||||
description = "🚀 The fastest WebAssembly interpreter. You can finally run WebAssembly on WebAssembly 😆"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/wasm3/wasm3"
|
||||
homepage = "https://github.com/wasm3/wasm3"
|
||||
|
||||
[[module]]
|
||||
name = "wasm3"
|
||||
source = "build/wasm3-wasi.wasm"
|
||||
abi = "wasi"
|
||||
|
||||
[[module]]
|
||||
name = "wasm3-strace"
|
||||
source = "build/wasm3-strace.wasm"
|
||||
abi = "wasi"
|
||||
|
||||
[[command]]
|
||||
name = "wasm3"
|
||||
module = "wasm3"
|
||||
|
||||
[[command]]
|
||||
name = "wasm3-strace"
|
||||
module = "wasm3-strace"
|
|
@ -0,0 +1,71 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created by AtomCrusher for the English Wikipedia -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
width="612"
|
||||
height="612"
|
||||
id="svg6"
|
||||
sodipodi:docname="wasm-symbol.svg"
|
||||
inkscape:version="0.92.3 (2405546, 2018-03-11)">
|
||||
<path
|
||||
d="m376 0c0 1.08 0 2.16 0 3.3 0 38.76-31.42 70.17-70.17 70.17-38.76 0-70.17-31.42-70.17-70.17l0 0c0-1.14 0-2.22 0-3.3L0 0l0 612 612 0 0-612z"
|
||||
id="path2"
|
||||
fill="#654ff0"
|
||||
style="fill:#000000" />
|
||||
<metadata
|
||||
id="metadata12">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs10" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1876"
|
||||
inkscape:window-height="1043"
|
||||
id="namedview8"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.54535033"
|
||||
inkscape:cx="194.41042"
|
||||
inkscape:cy="320.43749"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="flowRoot816" />
|
||||
<!-- Block -->
|
||||
<!-- Letters -->
|
||||
<g
|
||||
aria-label="3"
|
||||
transform="matrix(1.141234,0,0,1.141234,-389.85377,-108.04318)"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:40px;line-height:1.25;font-family:'padmaa-Bold.1.1';-inkscape-font-specification:'padmaa-Bold.1.1 Bold';letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none"
|
||||
id="flowRoot816">
|
||||
<path
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:333.33334351px;font-family:Keraleeyam;-inkscape-font-specification:'Keraleeyam Bold';fill:#ffffff;stroke-width:1.14123404"
|
||||
d="m 195.96484,329.72656 46.52149,216.68946 h 41 l 31.91015,-147.46875 h 0.7793 l 29.81055,147.46875 h 40.25 l 43.84765,-183.8125 h 74.51954 l -42.72266,57.02539 v 24.88867 c 6.06776,-1.11449 11.63969,-1.67188 16.7168,-1.67188 30.46262,0 45.69531,9.90761 45.69531,29.72071 0,26.1285 -16.2838,39.19336 -48.85156,39.19336 -13.993,0 -31.02128,-3.09552 -51.08203,-9.28711 v 35.47656 c 16.59346,5.82009 34.30255,8.73047 53.125,8.73047 24.64252,0 44.4558,-6.625 59.43945,-19.875 7.18224,-6.31542 12.69246,-13.93177 16.53125,-22.84766 3.83878,-8.91589 5.75781,-17.8302 5.75781,-26.74609 0,-35.04441 -17.64543,-55.29164 -52.9375,-60.74024 l 42.16406,-54.42382 v -32.32032 c -49.28217,0 -103.6131,0 -150.29297,0 l -31.56054,149.2793 h -0.58985 l -30.06054,-149.2793 h -37.93946 l -33.28125,147.47071 h -0.5 L 236.52539,329.72656 Z"
|
||||
transform="matrix(0.87624449,0,0,0.87624449,341.60722,94.672241)"
|
||||
id="path834"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccccccccccsssccscsscccccccccccc" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 28 KiB |
|
@ -0,0 +1,9 @@
|
|||
.gradle
|
||||
# Build output directies
|
||||
*/.externalNativeBuild
|
||||
/target
|
||||
*/target
|
||||
/build
|
||||
*/build
|
||||
*/.cxx
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
## Build for Android
|
||||
|
||||
<p align="center"><img width="50%" src="https://github.com/wasm3/wasm3/raw/main/extra/screenshot-android.png"></p>
|
||||
|
||||
Install OpenJDK 15, [Android Command Line Tools](https://developer.android.com/studio#cmdline-tools), then:
|
||||
|
||||
```sh
|
||||
export ANDROID_HOME=/opt/android-sdk/
|
||||
export PATH=$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$PATH
|
||||
```
|
||||
|
||||
Install NDK:
|
||||
```sh
|
||||
sdkmanager --install ndk-bundle platform-tools
|
||||
```
|
||||
|
||||
Build:
|
||||
```sh
|
||||
./gradlew build
|
||||
```
|
||||
|
||||
Install on device:
|
||||
```
|
||||
adb install -r ./app/build/outputs/apk/debug/app-debug.apk
|
||||
```
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
ndkVersion '22.1.7171670'
|
||||
|
||||
defaultConfig {
|
||||
applicationId 'com.example.wasm3'
|
||||
minSdkVersion 23
|
||||
targetSdkVersion 29
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'),
|
||||
'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
version '3.10.2+'
|
||||
path "src/main/cpp/CMakeLists.txt"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in /Users/gfan/dev/sdk_current/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.example.wasm3">
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity android:name=".MainActivity" android:configChanges="orientation|screenSize">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
|
@ -0,0 +1,16 @@
|
|||
cmake_minimum_required(VERSION 3.4.1)
|
||||
|
||||
add_definitions(-DANDROID -Wno-format-security -O3
|
||||
#-fno-optimize-sibling-calls
|
||||
-flto -fomit-frame-pointer -fno-stack-check -fno-stack-protector)
|
||||
|
||||
file(GLOB M3_SRC FOLLOW_SYMLINKS "m3/*.c" "*.c")
|
||||
|
||||
add_library(wasm3-jni SHARED ${M3_SRC})
|
||||
|
||||
set_target_properties(wasm3-jni PROPERTIES LINK_FLAGS "-flto -O3")
|
||||
|
||||
# Include libraries needed for wasm3-jni lib
|
||||
target_link_libraries(wasm3-jni
|
||||
android
|
||||
log)
|
|
@ -0,0 +1,96 @@
|
|||
//
|
||||
// Wasm3 - high performance WebAssembly interpreter written in C.
|
||||
//
|
||||
// Copyright © 2019 Steven Massey, Volodymyr Shymanskyy.
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <jni.h>
|
||||
|
||||
extern int main();
|
||||
|
||||
/*
|
||||
* JNI init
|
||||
*/
|
||||
|
||||
JavaVM* javaVM;
|
||||
JNIEnv* jniEnv;
|
||||
jclass activityClz;
|
||||
jobject activityObj;
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
JNI_OnLoad(JavaVM* vm, void* reserved)
|
||||
{
|
||||
if ((*vm)->GetEnv(vm, (void**)&jniEnv, JNI_VERSION_1_6) != JNI_OK) {
|
||||
return JNI_ERR; // JNI version not supported
|
||||
}
|
||||
javaVM = vm;
|
||||
return JNI_VERSION_1_6;
|
||||
}
|
||||
|
||||
static int pfd[2];
|
||||
static pthread_t pumpThread;
|
||||
static pthread_t mainThread;
|
||||
|
||||
static void* runOutputPump(void* ctx)
|
||||
{
|
||||
int readSize;
|
||||
char buff[128];
|
||||
|
||||
JNIEnv* env;
|
||||
(*javaVM)->AttachCurrentThread(javaVM, &env, NULL);
|
||||
|
||||
jmethodID outputTextId = (*env)->GetMethodID(env, activityClz,
|
||||
"outputText",
|
||||
"(Ljava/lang/String;)V");
|
||||
|
||||
while ((readSize = read(pfd[0], buff, sizeof(buff) - 1)) > 0)
|
||||
{
|
||||
buff[readSize] = '\0';
|
||||
|
||||
jstring javaMsg = (*env)->NewStringUTF(env, buff);
|
||||
(*env)->CallVoidMethod(env, activityObj, outputTextId, javaMsg);
|
||||
(*env)->DeleteLocalRef(env, javaMsg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void* runMain(void* ctx)
|
||||
{
|
||||
(*javaVM)->AttachCurrentThread(javaVM, &jniEnv, NULL);
|
||||
main();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_example_wasm3_MainActivity_runMain(JNIEnv* env, jobject instance)
|
||||
{
|
||||
setvbuf(stdout, 0, _IOLBF, 0); // stdout: line-buffered
|
||||
setvbuf(stderr, 0, _IONBF, 0); // stderr: unbuffered
|
||||
|
||||
// create the pipe and redirect stdout and stderr
|
||||
pipe(pfd);
|
||||
dup2(pfd[1], 1);
|
||||
dup2(pfd[1], 2);
|
||||
|
||||
jclass clz = (*env)->GetObjectClass(env, instance);
|
||||
activityClz = (*env)->NewGlobalRef(env, clz);
|
||||
activityObj = (*env)->NewGlobalRef(env, instance);
|
||||
|
||||
pthread_attr_t threadAttr;
|
||||
pthread_attr_init(&threadAttr);
|
||||
pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_DETACHED);
|
||||
|
||||
pthread_create( &pumpThread, &threadAttr, runOutputPump, NULL);
|
||||
|
||||
pthread_create( &mainThread, &threadAttr, runMain, NULL);
|
||||
|
||||
pthread_attr_destroy(&threadAttr);
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
../../../../../../source
|
|
@ -0,0 +1,70 @@
|
|||
//
|
||||
// Wasm3 - high performance WebAssembly interpreter written in C.
|
||||
//
|
||||
// Copyright © 2019 Steven Massey, Volodymyr Shymanskyy.
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "m3/wasm3.h"
|
||||
#include "m3/m3_api_libc.h"
|
||||
|
||||
#include "m3/extra/coremark_minimal.wasm.h"
|
||||
|
||||
#define FATAL(msg, ...) { printf("Fatal: " msg "\n", ##__VA_ARGS__); return; }
|
||||
|
||||
void run_wasm()
|
||||
{
|
||||
M3Result result = m3Err_none;
|
||||
|
||||
uint8_t* wasm = (uint8_t*)coremark_minimal_wasm;
|
||||
size_t fsize = coremark_minimal_wasm_len;
|
||||
|
||||
printf("Loading WebAssembly...\n");
|
||||
|
||||
IM3Environment env = m3_NewEnvironment ();
|
||||
if (!env) FATAL("m3_NewEnvironment failed");
|
||||
|
||||
IM3Runtime runtime = m3_NewRuntime (env, 8192, NULL);
|
||||
if (!runtime) FATAL("m3_NewRuntime failed");
|
||||
|
||||
IM3Module module;
|
||||
result = m3_ParseModule (env, &module, wasm, fsize);
|
||||
if (result) FATAL("m3_ParseModule: %s", result);
|
||||
|
||||
result = m3_LoadModule (runtime, module);
|
||||
if (result) FATAL("m3_LoadModule: %s", result);
|
||||
|
||||
result = m3_LinkLibC (module);
|
||||
if (result) FATAL("m3_LinkLibC: %s", result);
|
||||
|
||||
IM3Function f;
|
||||
result = m3_FindFunction (&f, runtime, "run");
|
||||
if (result) FATAL("m3_FindFunction: %s", result);
|
||||
|
||||
printf("Running CoreMark 1.0...\n");
|
||||
|
||||
result = m3_CallV (f);
|
||||
if (result) FATAL("m3_Call: %s", result);
|
||||
|
||||
float value = 0;
|
||||
result = m3_GetResultsV (f, &value);
|
||||
if (result) FATAL("m3_GetResults: %s", result);
|
||||
|
||||
printf("Result: %0.3f\n", value);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
printf("Wasm3 v" M3_VERSION " on Android (" M3_ARCH ")\n");
|
||||
printf("Build " __DATE__ " " __TIME__ "\n");
|
||||
|
||||
clock_t start = clock();
|
||||
run_wasm();
|
||||
clock_t end = clock();
|
||||
|
||||
printf("Elapsed: %ld ms\n", (end - start)*1000 / CLOCKS_PER_SEC);
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.example.wasm3;
|
||||
|
||||
import androidx.annotation.Keep;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import android.os.Bundle;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
TextView tv;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.activity_main);
|
||||
tv = (TextView)findViewById(R.id.console);
|
||||
|
||||
runMain();
|
||||
}
|
||||
|
||||
@Keep
|
||||
private void outputText(final String s) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
MainActivity.this.tv.append(s);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static {
|
||||
System.loadLibrary("wasm3-jni");
|
||||
}
|
||||
public native void runMain();
|
||||
}
|
||||
|
After Width: | Height: | Size: 6.1 KiB |
|
@ -0,0 +1,34 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/activity_main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@android:color/black"
|
||||
tools:context="com.example.wasm3.MainActivity">
|
||||
|
||||
<HorizontalScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scrollbars="none">
|
||||
|
||||
<ScrollView
|
||||
android:id="@+id/scroll_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="10dp"
|
||||
android:scrollbars="none">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/console"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:typeface = "monospace"
|
||||
android:textColor="@android:color/white"
|
||||
/>
|
||||
|
||||
</ScrollView>
|
||||
</HorizontalScrollView>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@android:color/black" />
|
||||
<foreground android:drawable="@drawable/ic_launcher" />
|
||||
</adaptive-icon>
|
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1011 B |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 3.7 KiB |
|
@ -0,0 +1,6 @@
|
|||
<resources>
|
||||
<!-- Example customization of dimensions originally defined in res/values/dimens.xml
|
||||
(such as screen margins) for screens with more than 820dp of available width. This
|
||||
would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
|
||||
<dimen name="activity_horizontal_margin">64dp</dimen>
|
||||
</resources>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#654ff0</color>
|
||||
<color name="colorPrimaryDark">#4b3bb3</color>
|
||||
<color name="colorAccent">#bfd9cc</color>
|
||||
</resources>
|
|
@ -0,0 +1,5 @@
|
|||
<resources>
|
||||
<!-- Default screen margins, per the Android Design guidelines. -->
|
||||
<dimen name="activity_horizontal_margin">16dp</dimen>
|
||||
<dimen name="activity_vertical_margin">16dp</dimen>
|
||||
</resources>
|
|
@ -0,0 +1,3 @@
|
|||
<resources>
|
||||
<string name="app_name">Wasm3</string>
|
||||
</resources>
|
|
@ -0,0 +1,11 @@
|
|||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
|
@ -0,0 +1,25 @@
|
|||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.0.0'
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
# Project-wide Gradle settings.
|
||||
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs=-Xmx1536m
|
||||
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
|
||||
android.enableJetifier=true
|
||||
android.useAndroidX=true
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.9.1-all.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
|
@ -0,0 +1,183 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
exec "$JAVACMD" "$@"
|
|
@ -0,0 +1,103 @@
|
|||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
|
@ -0,0 +1,43 @@
|
|||
|
||||
/*
|
||||
* Override printf, puts, putchar
|
||||
*/
|
||||
|
||||
JNIEnv* jniEnv;
|
||||
|
||||
void callOutputText(const char* text) {
|
||||
JNIEnv *env = jniEnv;
|
||||
|
||||
jstring javaMsg = (*env)->NewStringUTF(env, text);
|
||||
(*env)->CallVoidMethod(env, activityObj, outputTextId, javaMsg);
|
||||
(*env)->DeleteLocalRef(env, javaMsg);
|
||||
}
|
||||
|
||||
int printf(const char * format, ... )
|
||||
{
|
||||
char buff[256] = {};
|
||||
|
||||
va_list args;
|
||||
va_start (args, format);
|
||||
const int result = vsnprintf(buff, sizeof(buff), format, args);
|
||||
va_end (args);
|
||||
|
||||
if (result > 0) {
|
||||
callOutputText(buff);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int puts(const char *s)
|
||||
{
|
||||
callOutputText(s);
|
||||
callOutputText("\n");
|
||||
return strlen(s);
|
||||
}
|
||||
|
||||
int putchar(int c)
|
||||
{
|
||||
char buff[2] = { c, '\0' };
|
||||
callOutputText(buff);
|
||||
return c;
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
include ':app'
|
|
@ -0,0 +1 @@
|
|||
This is the main file for Linux, Windows, Mac OS, OpenWRT builds
|
|
@ -0,0 +1,723 @@
|
|||
//
|
||||
// Wasm3 - high performance WebAssembly interpreter written in C.
|
||||
//
|
||||
// Copyright © 2019 Steven Massey, Volodymyr Shymanskyy.
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "wasm3.h"
|
||||
#include "m3_api_libc.h"
|
||||
|
||||
#if defined(d_m3HasWASI) || defined(d_m3HasMetaWASI) || defined(d_m3HasUVWASI)
|
||||
#include "m3_api_wasi.h"
|
||||
#define LINK_WASI
|
||||
#endif
|
||||
|
||||
#if defined(d_m3HasTracer)
|
||||
#include "m3_api_tracer.h"
|
||||
#endif
|
||||
|
||||
// TODO: remove
|
||||
#include "m3_env.h"
|
||||
|
||||
/*
|
||||
* NOTE: Gas metering/limit only applies to pre-instrumented modules.
|
||||
* You can generate a metered version from any wasm file automatically, using
|
||||
* https://github.com/ewasm/wasm-metering
|
||||
*/
|
||||
#define GAS_LIMIT 500000000
|
||||
#define GAS_FACTOR 10000LL
|
||||
|
||||
#define MAX_MODULES 16
|
||||
|
||||
#define FATAL(msg, ...) { fprintf(stderr, "Error: [Fatal] " msg "\n", ##__VA_ARGS__); goto _onfatal; }
|
||||
|
||||
|
||||
static IM3Environment env;
|
||||
static IM3Runtime runtime;
|
||||
|
||||
static u8* wasm_bins[MAX_MODULES];
|
||||
static int wasm_bins_qty = 0;
|
||||
|
||||
#if defined(GAS_LIMIT)
|
||||
|
||||
static int64_t initial_gas = GAS_FACTOR * GAS_LIMIT;
|
||||
static int64_t current_gas = GAS_FACTOR * GAS_LIMIT;
|
||||
static bool is_gas_metered = false;
|
||||
|
||||
m3ApiRawFunction(metering_usegas)
|
||||
{
|
||||
m3ApiGetArg (int32_t, gas)
|
||||
|
||||
current_gas -= gas;
|
||||
|
||||
if (M3_UNLIKELY(current_gas < 0)) {
|
||||
m3ApiTrap("[trap] Out of gas");
|
||||
}
|
||||
m3ApiSuccess();
|
||||
}
|
||||
|
||||
#endif // GAS_LIMIT
|
||||
|
||||
|
||||
M3Result link_all (IM3Module module)
|
||||
{
|
||||
M3Result res;
|
||||
res = m3_LinkSpecTest (module);
|
||||
if (res) return res;
|
||||
|
||||
res = m3_LinkLibC (module);
|
||||
if (res) return res;
|
||||
|
||||
#if defined(LINK_WASI)
|
||||
res = m3_LinkWASI (module);
|
||||
if (res) return res;
|
||||
#endif
|
||||
|
||||
#if defined(d_m3HasTracer)
|
||||
res = m3_LinkTracer (module);
|
||||
if (res) return res;
|
||||
#endif
|
||||
|
||||
#if defined(GAS_LIMIT)
|
||||
res = m3_LinkRawFunction (module, "metering", "usegas", "v(i)", &metering_usegas);
|
||||
if (!res) {
|
||||
fprintf(stderr, "Warning: Gas is limited to %0.4f\n", (double)(current_gas) / GAS_FACTOR);
|
||||
is_gas_metered = true;
|
||||
}
|
||||
if (res == m3Err_functionLookupFailed) { res = NULL; }
|
||||
#endif
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
const char* modname_from_fn(const char* fn)
|
||||
{
|
||||
const char* sep = "/\\:*?";
|
||||
char c;
|
||||
while ((c = *sep++)) {
|
||||
const char* off = strrchr(fn, c) + 1;
|
||||
fn = (fn < off) ? off : fn;
|
||||
}
|
||||
return fn;
|
||||
}
|
||||
|
||||
M3Result repl_load (const char* fn)
|
||||
{
|
||||
M3Result result = m3Err_none;
|
||||
IM3Module module = NULL;
|
||||
|
||||
u8* wasm = NULL;
|
||||
u32 fsize = 0;
|
||||
|
||||
FILE* f = fopen (fn, "rb");
|
||||
if (!f) {
|
||||
return "cannot open file";
|
||||
}
|
||||
fseek (f, 0, SEEK_END);
|
||||
fsize = ftell(f);
|
||||
fseek (f, 0, SEEK_SET);
|
||||
|
||||
if (fsize < 8) {
|
||||
result = "file is too small";
|
||||
goto on_error;
|
||||
} else if (fsize > 64*1024*1024) {
|
||||
result = "file is too big";
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
wasm = (u8*) malloc(fsize);
|
||||
if (!wasm) {
|
||||
result = "cannot allocate memory for wasm binary";
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
if (fread (wasm, 1, fsize, f) != fsize) {
|
||||
result = "cannot read file";
|
||||
goto on_error;
|
||||
}
|
||||
fclose (f);
|
||||
f = NULL;
|
||||
|
||||
result = m3_ParseModule (env, &module, wasm, fsize);
|
||||
if (result) goto on_error;
|
||||
|
||||
result = m3_LoadModule (runtime, module);
|
||||
if (result) goto on_error;
|
||||
|
||||
m3_SetModuleName(module, modname_from_fn(fn));
|
||||
|
||||
result = link_all (module);
|
||||
if (result) goto on_error;
|
||||
|
||||
if (wasm_bins_qty < MAX_MODULES) {
|
||||
wasm_bins[wasm_bins_qty++] = wasm;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
on_error:
|
||||
m3_FreeModule(module);
|
||||
if (wasm) free(wasm);
|
||||
if (f) fclose(f);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
M3Result repl_load_hex (u32 fsize)
|
||||
{
|
||||
M3Result result = m3Err_none;
|
||||
|
||||
if (fsize < 8) {
|
||||
return "file is too small";
|
||||
} else if (fsize > 10*1024*1024) {
|
||||
return "file too big";
|
||||
}
|
||||
|
||||
u8* wasm = (u8*) malloc(fsize);
|
||||
if (!wasm) {
|
||||
return "cannot allocate memory for wasm binary";
|
||||
}
|
||||
|
||||
{ // Load hex data from stdin
|
||||
u32 wasm_idx = 0;
|
||||
char hex[3] = { 0, };
|
||||
int hex_idx = 0;
|
||||
while (wasm_idx < fsize) {
|
||||
int c = fgetc(stdin);
|
||||
if (!isxdigit(c)) continue; // Skip non-hex chars
|
||||
hex[hex_idx++] = c;
|
||||
if (hex_idx == 2) {
|
||||
int val = strtol(hex, NULL, 16);
|
||||
wasm[wasm_idx++] = val;
|
||||
hex_idx = 0;
|
||||
}
|
||||
}
|
||||
if (!fgets(hex, 3, stdin)) // Consume a newline
|
||||
return "cannot read EOL";
|
||||
}
|
||||
|
||||
IM3Module module;
|
||||
result = m3_ParseModule (env, &module, wasm, fsize);
|
||||
if (result) return result;
|
||||
|
||||
result = m3_LoadModule (runtime, module);
|
||||
if (result) return result;
|
||||
|
||||
result = link_all (module);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void print_gas_used()
|
||||
{
|
||||
#if defined(GAS_LIMIT)
|
||||
if (is_gas_metered) {
|
||||
fprintf(stderr, "Gas used: %0.4f\n", (double)(initial_gas - current_gas) / GAS_FACTOR);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void print_backtrace()
|
||||
{
|
||||
IM3BacktraceInfo info = m3_GetBacktrace(runtime);
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(stderr, "==== wasm backtrace:");
|
||||
|
||||
int frameCount = 0;
|
||||
IM3BacktraceFrame curr = info->frames;
|
||||
while (curr)
|
||||
{
|
||||
fprintf(stderr, "\n %d: 0x%06x - %s!%s",
|
||||
frameCount, curr->moduleOffset,
|
||||
m3_GetModuleName (m3_GetFunctionModule(curr->function)),
|
||||
m3_GetFunctionName (curr->function)
|
||||
);
|
||||
curr = curr->next;
|
||||
frameCount++;
|
||||
}
|
||||
if (info->lastFrame == M3_BACKTRACE_TRUNCATED) {
|
||||
fprintf(stderr, "\n (truncated)");
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
M3Result repl_call (const char* name, int argc, const char* argv[])
|
||||
{
|
||||
IM3Function func;
|
||||
M3Result result = m3_FindFunction (&func, runtime, name);
|
||||
if (result) return result;
|
||||
|
||||
if (argc && (!strcmp(name, "main") || !strcmp(name, "_main"))) {
|
||||
return "passing arguments to libc main() not implemented";
|
||||
}
|
||||
|
||||
if (!strcmp(name, "_start")) {
|
||||
#if defined(LINK_WASI)
|
||||
// Strip wasm file path
|
||||
if (argc > 0) {
|
||||
argv[0] = modname_from_fn(argv[0]);
|
||||
}
|
||||
|
||||
m3_wasi_context_t* wasi_ctx = m3_GetWasiContext();
|
||||
wasi_ctx->argc = argc;
|
||||
wasi_ctx->argv = argv;
|
||||
|
||||
result = m3_CallArgv(func, 0, NULL);
|
||||
|
||||
print_gas_used();
|
||||
|
||||
if (result == m3Err_trapExit) {
|
||||
exit(wasi_ctx->exit_code);
|
||||
}
|
||||
|
||||
return result;
|
||||
#else
|
||||
return "WASI not linked";
|
||||
#endif
|
||||
}
|
||||
|
||||
int arg_count = m3_GetArgCount(func);
|
||||
int ret_count = m3_GetRetCount(func);
|
||||
if (argc < arg_count) {
|
||||
return "not enough arguments";
|
||||
} else if (argc > arg_count) {
|
||||
return "too many arguments";
|
||||
}
|
||||
|
||||
result = m3_CallArgv (func, argc, argv);
|
||||
|
||||
print_gas_used();
|
||||
|
||||
if (result) return result;
|
||||
|
||||
static uint64_t valbuff[128];
|
||||
static const void* valptrs[128];
|
||||
memset(valbuff, 0, sizeof(valbuff));
|
||||
for (int i = 0; i < ret_count; i++) {
|
||||
valptrs[i] = &valbuff[i];
|
||||
}
|
||||
result = m3_GetResults (func, ret_count, valptrs);
|
||||
if (result) return result;
|
||||
|
||||
if (ret_count <= 0) {
|
||||
fprintf (stderr, "Result: <Empty Stack>\n");
|
||||
}
|
||||
for (int i = 0; i < ret_count; i++) {
|
||||
switch (m3_GetRetType(func, i)) {
|
||||
case c_m3Type_i32: fprintf (stderr, "Result: %" PRIi32 "\n", *(i32*)valptrs[i]); break;
|
||||
case c_m3Type_i64: fprintf (stderr, "Result: %" PRIi64 "\n", *(i64*)valptrs[i]); break;
|
||||
# if d_m3HasFloat
|
||||
case c_m3Type_f32: fprintf (stderr, "Result: %" PRIf32 "\n", *(f32*)valptrs[i]); break;
|
||||
case c_m3Type_f64: fprintf (stderr, "Result: %" PRIf64 "\n", *(f64*)valptrs[i]); break;
|
||||
# endif
|
||||
default: return "unknown return type";
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// :invoke is used by spec tests, so it treats floats as raw data
|
||||
M3Result repl_invoke (const char* name, int argc, const char* argv[])
|
||||
{
|
||||
IM3Function func;
|
||||
M3Result result = m3_FindFunction (&func, runtime, name);
|
||||
if (result) return result;
|
||||
|
||||
int arg_count = m3_GetArgCount(func);
|
||||
int ret_count = m3_GetRetCount(func);
|
||||
|
||||
if (argc > 128) {
|
||||
return "arguments limit reached";
|
||||
} else if (argc < arg_count) {
|
||||
return "not enough arguments";
|
||||
} else if (argc > arg_count) {
|
||||
return "too many arguments";
|
||||
}
|
||||
|
||||
static uint64_t valbuff[128];
|
||||
static const void* valptrs[128];
|
||||
memset(valbuff, 0, sizeof(valbuff));
|
||||
memset(valptrs, 0, sizeof(valptrs));
|
||||
|
||||
for (int i = 0; i < argc; i++) {
|
||||
u64* s = &valbuff[i];
|
||||
valptrs[i] = s;
|
||||
switch (m3_GetArgType(func, i)) {
|
||||
case c_m3Type_i32:
|
||||
case c_m3Type_f32: *(u32*)(s) = strtoul(argv[i], NULL, 10); break;
|
||||
case c_m3Type_i64:
|
||||
case c_m3Type_f64: *(u64*)(s) = strtoull(argv[i], NULL, 10); break;
|
||||
default: return "unknown argument type";
|
||||
}
|
||||
}
|
||||
|
||||
result = m3_Call (func, argc, valptrs);
|
||||
if (result) return result;
|
||||
|
||||
// reuse valbuff for return values
|
||||
memset(valbuff, 0, sizeof(valbuff));
|
||||
for (int i = 0; i < ret_count; i++) {
|
||||
valptrs[i] = &valbuff[i];
|
||||
}
|
||||
result = m3_GetResults (func, ret_count, valptrs);
|
||||
if (result) return result;
|
||||
|
||||
fprintf (stderr, "Result: ");
|
||||
if (ret_count <= 0) {
|
||||
fprintf (stderr, "<Empty Stack>");
|
||||
}
|
||||
for (int i = 0; i < ret_count; i++) {
|
||||
switch (m3_GetRetType(func, i)) {
|
||||
case c_m3Type_i32: fprintf (stderr, "%" PRIu32 ":i32", *(u32*)valptrs[i]); break;
|
||||
case c_m3Type_f32: fprintf (stderr, "%" PRIu32 ":f32", *(u32*)valptrs[i]); break;
|
||||
case c_m3Type_i64: fprintf (stderr, "%" PRIu64 ":i64", *(u64*)valptrs[i]); break;
|
||||
case c_m3Type_f64: fprintf (stderr, "%" PRIu64 ":f64", *(u64*)valptrs[i]); break;
|
||||
default: return "unknown return type";
|
||||
}
|
||||
if (i != ret_count-1) {
|
||||
fprintf (stderr, ", ");
|
||||
}
|
||||
}
|
||||
fprintf (stderr, "\n");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
M3Result repl_global_get (const char* name)
|
||||
{
|
||||
IM3Global g = m3_FindGlobal(runtime->modules, name);
|
||||
|
||||
M3TaggedValue tagged;
|
||||
M3Result err = m3_GetGlobal (g, &tagged);
|
||||
if (err) return err;
|
||||
|
||||
switch (tagged.type) {
|
||||
case c_m3Type_i32: fprintf (stderr, "%" PRIu32 ":i32\n", tagged.value.i32); break;
|
||||
case c_m3Type_i64: fprintf (stderr, "%" PRIu64 ":i64\n", tagged.value.i64); break;
|
||||
case c_m3Type_f32: fprintf (stderr, "%" PRIf32 ":f32\n", tagged.value.f32); break;
|
||||
case c_m3Type_f64: fprintf (stderr, "%" PRIf64 ":f64\n", tagged.value.f64); break;
|
||||
default: return m3Err_invalidTypeId;
|
||||
}
|
||||
return m3Err_none;
|
||||
}
|
||||
|
||||
M3Result repl_global_set (const char* name, const char* value)
|
||||
{
|
||||
IM3Global g = m3_FindGlobal(runtime->modules, name);
|
||||
|
||||
M3TaggedValue tagged = {
|
||||
.type = m3_GetGlobalType(g)
|
||||
};
|
||||
|
||||
switch (tagged.type) {
|
||||
case c_m3Type_i32: tagged.value.i32 = strtoul(value, NULL, 10); break;
|
||||
case c_m3Type_i64: tagged.value.i64 = strtoull(value, NULL, 10); break;
|
||||
case c_m3Type_f32: tagged.value.f32 = strtod(value, NULL); break;
|
||||
case c_m3Type_f64: tagged.value.f64 = strtod(value, NULL); break;
|
||||
default: return m3Err_invalidTypeId;
|
||||
}
|
||||
|
||||
return m3_SetGlobal (g, &tagged);
|
||||
}
|
||||
|
||||
M3Result repl_compile ()
|
||||
{
|
||||
return m3_CompileModule(runtime->modules);
|
||||
}
|
||||
|
||||
M3Result repl_dump ()
|
||||
{
|
||||
uint32_t len;
|
||||
uint8_t* mem = m3_GetMemory(runtime, &len, 0);
|
||||
if (mem) {
|
||||
FILE* f = fopen ("wasm3_dump.bin", "wb");
|
||||
if (!f) {
|
||||
return "cannot open file";
|
||||
}
|
||||
if (fwrite (mem, 1, len, f) != len) {
|
||||
return "cannot write file";
|
||||
}
|
||||
fclose (f);
|
||||
}
|
||||
return m3Err_none;
|
||||
}
|
||||
|
||||
void repl_free ()
|
||||
{
|
||||
if (runtime) {
|
||||
m3_FreeRuntime (runtime);
|
||||
runtime = NULL;
|
||||
}
|
||||
|
||||
for (int i = 0; i < wasm_bins_qty; i++) {
|
||||
free (wasm_bins[i]);
|
||||
wasm_bins[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
M3Result repl_init (unsigned stack)
|
||||
{
|
||||
repl_free();
|
||||
runtime = m3_NewRuntime (env, stack, NULL);
|
||||
if (runtime == NULL) {
|
||||
return "m3_NewRuntime failed";
|
||||
}
|
||||
return m3Err_none;
|
||||
}
|
||||
|
||||
static
|
||||
void unescape(char* buff)
|
||||
{
|
||||
char* outp = buff;
|
||||
while (*buff) {
|
||||
if (*buff == '\\') {
|
||||
switch (*(buff+1)) {
|
||||
case '0': *outp++ = '\0'; break;
|
||||
case 'b': *outp++ = '\b'; break;
|
||||
case 'n': *outp++ = '\n'; break;
|
||||
case 'r': *outp++ = '\r'; break;
|
||||
case 't': *outp++ = '\t'; break;
|
||||
case 'x': {
|
||||
char hex[3] = { *(buff+2), *(buff+3), '\0' };
|
||||
*outp = strtol(hex, NULL, 16);
|
||||
buff += 2; outp += 1;
|
||||
break;
|
||||
}
|
||||
// Otherwise just pass the letter
|
||||
// Also handles '\\'
|
||||
default: *outp++ = *(buff+1); break;
|
||||
}
|
||||
buff += 2;
|
||||
} else {
|
||||
*outp++ = *buff++;
|
||||
}
|
||||
}
|
||||
*outp = '\0';
|
||||
}
|
||||
|
||||
static
|
||||
int split_argv(char *str, char** argv)
|
||||
{
|
||||
int result = 0;
|
||||
char* curr = str;
|
||||
int len = 0;
|
||||
for (int i = 0; str[i] != '\0'; i++) {
|
||||
if (strchr(" \n\r\t", str[i])) {
|
||||
if (len) { // Found space after non-space
|
||||
str[i] = '\0';
|
||||
//unescape(curr); // TODO: breaks windows build?
|
||||
argv[result++] = curr;
|
||||
len = 0;
|
||||
}
|
||||
} else {
|
||||
if (!len) { // Found non-space after space
|
||||
curr = &str[i];
|
||||
}
|
||||
len++;
|
||||
}
|
||||
}
|
||||
argv[result] = NULL;
|
||||
return result;
|
||||
}
|
||||
|
||||
void print_version() {
|
||||
const char* wasm3_env = getenv("WASM3");
|
||||
const char* wasm3_arch = getenv("WASM3_ARCH");
|
||||
|
||||
printf("Wasm3 v" M3_VERSION "%s on %s\n",
|
||||
(wasm3_arch || wasm3_env) ? " self-hosting" : "",
|
||||
wasm3_arch ? wasm3_arch : M3_ARCH);
|
||||
|
||||
printf("Build: " __DATE__ " " __TIME__ ", " M3_COMPILER_VER "\n");
|
||||
}
|
||||
|
||||
void print_usage() {
|
||||
puts("Usage:");
|
||||
puts(" wasm3 [options] <file> [args...]");
|
||||
puts(" wasm3 --repl [file]");
|
||||
puts("Options:");
|
||||
puts(" --func <function> function to run default: _start");
|
||||
puts(" --stack-size <size> stack size in bytes default: 64KB");
|
||||
puts(" --compile disable lazy compilation");
|
||||
puts(" --dump-on-trap dump wasm memory");
|
||||
puts(" --gas-limit set gas limit");
|
||||
}
|
||||
|
||||
#define ARGV_SHIFT() { i_argc--; i_argv++; }
|
||||
#define ARGV_SET(x) { if (i_argc > 0) { x = i_argv[0]; ARGV_SHIFT(); } }
|
||||
|
||||
int main (int i_argc, const char* i_argv[])
|
||||
{
|
||||
M3Result result = m3Err_none;
|
||||
env = m3_NewEnvironment ();
|
||||
runtime = NULL;
|
||||
|
||||
bool argRepl = false;
|
||||
bool argDumpOnTrap = false;
|
||||
bool argCompile = false;
|
||||
const char* argFile = NULL;
|
||||
const char* argFunc = "_start";
|
||||
unsigned argStackSize = 64*1024;
|
||||
|
||||
// m3_PrintM3Info ();
|
||||
|
||||
ARGV_SHIFT(); // Skip executable name
|
||||
|
||||
while (i_argc > 0)
|
||||
{
|
||||
const char* arg = i_argv[0];
|
||||
if (arg[0] != '-') break;
|
||||
|
||||
ARGV_SHIFT();
|
||||
if (!strcmp("--help", arg) or !strcmp("-h", arg)) {
|
||||
print_usage();
|
||||
return 0;
|
||||
} else if (!strcmp("--version", arg)) {
|
||||
print_version();
|
||||
return 0;
|
||||
} else if (!strcmp("--repl", arg)) {
|
||||
argRepl = true;
|
||||
} else if (!strcmp("--dump-on-trap", arg)) {
|
||||
argDumpOnTrap = true;
|
||||
} else if (!strcmp("--compile", arg)) {
|
||||
argCompile = true;
|
||||
} else if (!strcmp("--stack-size", arg)) {
|
||||
const char* tmp = "65536";
|
||||
ARGV_SET(tmp);
|
||||
argStackSize = atol(tmp);
|
||||
} else if (!strcmp("--gas-limit", arg)) {
|
||||
const char* tmp = "0";
|
||||
ARGV_SET(tmp);
|
||||
initial_gas = current_gas = GAS_FACTOR * atol(tmp);
|
||||
} else if (!strcmp("--dir", arg)) {
|
||||
const char* argDir;
|
||||
ARGV_SET(argDir);
|
||||
(void)argDir;
|
||||
} else if (!strcmp("--func", arg) or !strcmp("-f", arg)) {
|
||||
ARGV_SET(argFunc);
|
||||
}
|
||||
}
|
||||
|
||||
if ((argRepl and (i_argc > 1)) or // repl supports 0 or 1 args
|
||||
(not argRepl and (i_argc < 1)) // normal expects at least 1
|
||||
) {
|
||||
print_usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
ARGV_SET(argFile);
|
||||
|
||||
result = repl_init(argStackSize);
|
||||
if (result) FATAL("repl_init: %s", result);
|
||||
|
||||
if (argFile) {
|
||||
result = repl_load(argFile);
|
||||
if (result) FATAL("repl_load: %s", result);
|
||||
|
||||
if (argCompile) {
|
||||
repl_compile();
|
||||
}
|
||||
|
||||
if (argFunc and not argRepl) {
|
||||
if (!strcmp(argFunc, "_start")) {
|
||||
// When passing args to WASI, include wasm filename as argv[0]
|
||||
result = repl_call(argFunc, i_argc+1, i_argv-1);
|
||||
} else {
|
||||
result = repl_call(argFunc, i_argc, i_argv);
|
||||
}
|
||||
|
||||
if (result) {
|
||||
if (argDumpOnTrap) {
|
||||
repl_dump();
|
||||
}
|
||||
print_backtrace();
|
||||
goto _onfatal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (argRepl)
|
||||
{
|
||||
char cmd_buff[2048] = { 0, };
|
||||
char* argv[32] = { 0, };
|
||||
fprintf(stdout, "wasm3> ");
|
||||
fflush(stdout);
|
||||
if (!fgets(cmd_buff, sizeof(cmd_buff), stdin)) {
|
||||
return 0;
|
||||
}
|
||||
int argc = split_argv(cmd_buff, argv);
|
||||
if (argc <= 0) {
|
||||
continue;
|
||||
}
|
||||
result = m3Err_none;
|
||||
if (!strcmp(":init", argv[0])) {
|
||||
result = repl_init(argStackSize);
|
||||
} else if (!strcmp(":version", argv[0])) {
|
||||
print_version();
|
||||
} else if (!strcmp(":exit", argv[0])) {
|
||||
repl_free();
|
||||
return 0;
|
||||
} else if (!strcmp(":load", argv[0])) { // :load <filename>
|
||||
result = repl_load(argv[1]);
|
||||
} else if (!strcmp(":load-hex", argv[0])) { // :load-hex <size>\n <hex-encoded-binary>
|
||||
result = repl_load_hex(atol(argv[1]));
|
||||
} else if (!strcmp(":get-global", argv[0])) {
|
||||
result = repl_global_get(argv[1]);
|
||||
} else if (!strcmp(":set-global", argv[0])) {
|
||||
result = repl_global_set(argv[1], argv[2]);
|
||||
} else if (!strcmp(":dump", argv[0])) {
|
||||
result = repl_dump();
|
||||
} else if (!strcmp(":compile", argv[0])) {
|
||||
result = repl_compile();
|
||||
} else if (!strcmp(":invoke", argv[0])) {
|
||||
unescape(argv[1]);
|
||||
result = repl_invoke(argv[1], argc-2, (const char**)(argv+2));
|
||||
} else if (argv[0][0] == ':') {
|
||||
result = "no such command";
|
||||
} else {
|
||||
unescape(argv[0]);
|
||||
result = repl_call(argv[0], argc-1, (const char**)(argv+1));
|
||||
if (result) {
|
||||
print_backtrace();
|
||||
}
|
||||
}
|
||||
|
||||
if (result == m3Err_trapExit) {
|
||||
//TODO: fprintf(stderr, M3_ARCH "-wasi: exit(%d)\n", runtime->exit_code);
|
||||
} else if (result) {
|
||||
fprintf (stderr, "Error: %s", result);
|
||||
M3ErrorInfo info;
|
||||
m3_GetErrorInfo (runtime, &info);
|
||||
fprintf (stderr, " (%s)\n", info.message);
|
||||
}
|
||||
}
|
||||
|
||||
_onfatal:
|
||||
if (result) {
|
||||
fprintf (stderr, "Error: %s", result);
|
||||
if (runtime)
|
||||
{
|
||||
M3ErrorInfo info;
|
||||
m3_GetErrorInfo (runtime, &info);
|
||||
if (strlen(info.message)) {
|
||||
fprintf (stderr, " (%s)", info.message);
|
||||
}
|
||||
}
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
|
||||
m3_FreeRuntime (runtime);
|
||||
m3_FreeEnvironment (env);
|
||||
|
||||
return result ? 1 : 0;
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
//
|
||||
// Wasm3 - high performance WebAssembly interpreter written in C.
|
||||
//
|
||||
// Copyright © 2019 Steven Massey, Volodymyr Shymanskyy.
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "wasm3.h"
|
||||
|
||||
#define FATAL(...) __builtin_trap()
|
||||
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
|
||||
{
|
||||
M3Result result = m3Err_none;
|
||||
|
||||
if (size < 8 || size > 256*1024) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
IM3Environment env = m3_NewEnvironment ();
|
||||
if (env) {
|
||||
IM3Runtime runtime = m3_NewRuntime (env, 128, NULL);
|
||||
if (runtime) {
|
||||
IM3Module module = NULL;
|
||||
result = m3_ParseModule (env, &module, data, size);
|
||||
if (module) {
|
||||
result = m3_LoadModule (runtime, module);
|
||||
if (result == 0) {
|
||||
IM3Function f = NULL;
|
||||
result = m3_FindFunction (&f, runtime, "fib");
|
||||
/* TODO:
|
||||
if (f) {
|
||||
m3_CallV (f, 10);
|
||||
}*/
|
||||
} else {
|
||||
m3_FreeModule (module);
|
||||
}
|
||||
}
|
||||
|
||||
m3_FreeRuntime(runtime);
|
||||
}
|
||||
m3_FreeEnvironment(env);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
cosmopolitan/
|
||||
wasm3.com
|
||||
wasm3.com.dbg
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
|
||||
COSMOPOLITAN_VERSION=1.0
|
||||
COSMOPOLITAN_URL=https://github.com/jart/cosmopolitan/releases/download/$COSMOPOLITAN_VERSION/cosmopolitan-amalgamation-$COSMOPOLITAN_VERSION.zip
|
||||
|
||||
SOURCE_DIR=../../source
|
||||
|
||||
EXTRA_FLAGS="-Dd_m3PreferStaticAlloc -Dd_m3HasWASI"
|
||||
|
||||
STD=./cosmopolitan/std
|
||||
|
||||
if [ ! -d "./cosmopolitan" ]; then
|
||||
echo "Downloading Cosmopolitan..."
|
||||
curl -L -o cosmopolitan.zip $COSMOPOLITAN_URL
|
||||
unzip cosmopolitan.zip -d cosmopolitan
|
||||
rm cosmopolitan.zip
|
||||
fi
|
||||
|
||||
if [ ! -d "$STD/sys" ]; then
|
||||
# Generate header stubs
|
||||
mkdir -p $STD/sys
|
||||
touch $STD/assert.h $STD/limits.h $STD/ctype.h $STD/time.h $STD/errno.h \
|
||||
$STD/inttypes.h $STD/fcntl.h $STD/math.h $STD/stdarg.h $STD/stdbool.h \
|
||||
$STD/stdint.h $STD/stdio.h $STD/stdlib.h $STD/string.h $STD/stddef.h \
|
||||
$STD/sys/types.h $STD/sys/stat.h $STD/unistd.h $STD/sys/uio.h \
|
||||
$STD/sys/random.h
|
||||
fi
|
||||
|
||||
echo "Building Wasm3..."
|
||||
|
||||
# TODO: remove -fno-strict-aliasing
|
||||
|
||||
gcc -g -Os -static -nostdlib -nostdinc -fno-pie -no-pie -mno-red-zone \
|
||||
-Wl,--gc-sections -Wl,-z,max-page-size=0x1000 -fuse-ld=bfd \
|
||||
-Wl,-T,cosmopolitan/ape.lds -include cosmopolitan/cosmopolitan.h \
|
||||
-Wno-format-security -Wfatal-errors -fno-strict-aliasing $EXTRA_FLAGS \
|
||||
-fomit-frame-pointer -fno-stack-check -fno-stack-protector \
|
||||
-o wasm3.com.dbg -DAPE -I$STD -I$SOURCE_DIR $SOURCE_DIR/*.c ../app/main.c \
|
||||
cosmopolitan/crt.o cosmopolitan/ape.o cosmopolitan/cosmopolitan.a
|
||||
|
||||
objcopy -SO binary wasm3.com.dbg wasm3.com
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
build
|
||||
cmake-build-debug
|
|
@ -0,0 +1,25 @@
|
|||
cmake_minimum_required(VERSION 3.9)
|
||||
project(wasm3_cpp_example)
|
||||
|
||||
set(target ${CMAKE_PROJECT_NAME})
|
||||
|
||||
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/../../source ${CMAKE_BINARY_DIR}/m3)
|
||||
|
||||
add_executable(${target} main.cpp)
|
||||
target_link_libraries(${target} PRIVATE m3)
|
||||
|
||||
add_subdirectory(wasm3_cpp)
|
||||
target_link_libraries(${target} PRIVATE wasm3_cpp)
|
||||
|
||||
target_compile_options(${target} PUBLIC -g)
|
||||
target_compile_options(m3 PUBLIC -g)
|
||||
|
||||
# Copy the 'wasm' directory into the build directory, so that
|
||||
# wasm/test_prog.wasm is found even if wasm3_cpp_example is executed
|
||||
# from the build directory.
|
||||
add_custom_target(copy_wasm ALL
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CMAKE_CURRENT_LIST_DIR}/wasm
|
||||
${CMAKE_BINARY_DIR}/wasm
|
||||
)
|
||||
add_dependencies(${CMAKE_PROJECT_NAME} copy_wasm)
|
|
@ -0,0 +1,98 @@
|
|||
## C++ wrapper
|
||||
|
||||
This example shows how to embed WASM3 into a C++ application. It uses a header-only library, `wasm3_cpp.h`, provided in `wasm3_cpp` subdirectory. Like WASM3 itself, this library can be included into CMake projects using `add_subdirectory` function.
|
||||
|
||||
The main code of the example in `main.cpp` initializes WASM3, loads a WebAssembly module, links two external functions to the module, and executes two functions defined in WebAssembly.
|
||||
|
||||
The WebAssembly module source code is inside `wasm` subdirectory.
|
||||
|
||||
### `wasm3_cpp.h` reference
|
||||
|
||||
All the classes are located in `wasm3` namespace.
|
||||
|
||||
#### Class `environment`
|
||||
|
||||
`environment::environment()` — create a new WASM3 environment. Runtimes, modules are owned by an environment.
|
||||
|
||||
`runtime environment::new_runtime(size_t stack_size_bytes)` — create new runtime inside the environment.
|
||||
|
||||
`module environment::parse_module(std::istream &in)` or `module environment::parse_module(const uint8_t *data, size_t size)` — parse a WASM binary module.
|
||||
|
||||
#### Class `runtime`
|
||||
|
||||
`runtime` objects are created using `environment::new_runtime` method, see above.
|
||||
|
||||
`void runtime::load(module &m)` — load a parsed module into the runtime.
|
||||
|
||||
`function runtime::find_function(const char *name)` — find a function defined in one of the loaded modules, by name. Raises a `wasm3::error` exception if the function is not found.
|
||||
|
||||
#### Class `module`
|
||||
|
||||
`module` objects are created by `environment::parse_module`. Parsed modules can be loaded into a `runtime` object. One module can only be loaded into one runtime.
|
||||
|
||||
Before loading a module, you may need to link some external functions to it:
|
||||
|
||||
`template <Func> void module::link(const char *mod, const char *function_name, Func *function)` — link a function `function` to module named `mod` under the name `function_name`. To link to any module, use `mod="*"`.
|
||||
|
||||
`function` has to be either a non-member function or a static member function.
|
||||
|
||||
Currently, the following types of arguments can be passed to functions linked this way:
|
||||
|
||||
* int32_t
|
||||
* int64_t
|
||||
* float
|
||||
* double
|
||||
* const/non-const pointers
|
||||
|
||||
Automatic conversion of other integral types may be implemented in the future.
|
||||
|
||||
If the module doesn't reference an imported function named `func`, an exception is thrown. To link a function "optionally", i.e. without throwing an exception if the function is not imported, use `module::link_optional` instead.
|
||||
|
||||
#### Class `function`
|
||||
|
||||
`function` object can be obtained from a `runtime`, looking up the function by name. Function objects are used to call WebAssembly functions.
|
||||
|
||||
`template <typename Ret = void, typename ...Args> Ret function::call(Args...)` — calls a WebAssembly function with or without arguments and a return value.<br>
|
||||
The return value of the function, if not `void`, is automatically converted to the type `Ret`.<br>
|
||||
Note that you always need to specify the matching return type when using this template with a non-void function.<br>
|
||||
Examples:
|
||||
```cpp
|
||||
// WASM signature: [] → []
|
||||
func.call();
|
||||
|
||||
// WASM signature: [i32, i32] → []
|
||||
func.call(42, 43); // implicit argument types
|
||||
|
||||
// WASM signature: [i32, i64] → []
|
||||
func.call<void, int32_t, int64_t>(42, 43); // explicit argument types require the return type
|
||||
|
||||
// WASM signature: [] → [i32]
|
||||
auto result = func.call<int32_t>();
|
||||
|
||||
// WASM signature: [i32, i32] → [i64]
|
||||
auto result = func.call<int64_t>(42, 43); // implicit argument types
|
||||
|
||||
// WASM signature: [i32, i64] → [i64]
|
||||
auto result = func.call<int64_t, int32_t, int64_t>(42, 43); // explicit argument types
|
||||
|
||||
```
|
||||
|
||||
`template <typename Ret, typename ...Args> Ret function::call_argv(Args...)` — same as above, except that this function takes arguments as C strings (`const char*`).
|
||||
|
||||
### Building and running
|
||||
|
||||
This directory is a CMake project, and can be built as follows:
|
||||
|
||||
```bash
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
cmake --build .
|
||||
```
|
||||
|
||||
Then run the example:
|
||||
|
||||
```bash
|
||||
./wasm3_cpp_example
|
||||
```
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
|
||||
#include "wasm3_cpp.h"
|
||||
#include "wasm/test_prog.wasm.h"
|
||||
|
||||
int sum(int a, int b)
|
||||
{
|
||||
return a + b;
|
||||
}
|
||||
|
||||
void * ext_memcpy (void* dst, const void* arg, int32_t size)
|
||||
{
|
||||
return memcpy(dst, arg, (size_t) size);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
std::cout << "Loading WebAssembly..." << std::endl;
|
||||
|
||||
/* Wasm module can be loaded from a file */
|
||||
try {
|
||||
wasm3::environment env;
|
||||
wasm3::runtime runtime = env.new_runtime(1024);
|
||||
const char* file_name = "wasm/test_prog.wasm";
|
||||
std::ifstream wasm_file(file_name, std::ios::binary | std::ios::in);
|
||||
if (!wasm_file.is_open()) {
|
||||
throw std::runtime_error("Failed to open wasm file");
|
||||
}
|
||||
wasm3::module mod = env.parse_module(wasm_file);
|
||||
runtime.load(mod);
|
||||
}
|
||||
catch(std::runtime_error &e) {
|
||||
std::cerr << "WASM3 error: " << e.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Wasm module can also be loaded from an array */
|
||||
try {
|
||||
wasm3::environment env;
|
||||
wasm3::runtime runtime = env.new_runtime(1024);
|
||||
wasm3::module mod = env.parse_module(test_prog_wasm, test_prog_wasm_len);
|
||||
runtime.load(mod);
|
||||
|
||||
mod.link("*", "sum", sum);
|
||||
mod.link("*", "ext_memcpy", ext_memcpy);
|
||||
|
||||
{
|
||||
wasm3::function test_fn = runtime.find_function("test");
|
||||
auto res = test_fn.call<int>(20, 10);
|
||||
std::cout << "result: " << res << std::endl;
|
||||
}
|
||||
{
|
||||
wasm3::function memcpy_test_fn = runtime.find_function("test_memcpy");
|
||||
auto res = memcpy_test_fn.call<int64_t>();
|
||||
std::cout << "result: 0x" << std::hex << res << std::dec << std::endl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calling functions that modify an internal state, with mixed argument / return types
|
||||
*/
|
||||
{
|
||||
wasm3::function counter_get_fn = runtime.find_function("test_counter_get");
|
||||
wasm3::function counter_inc_fn = runtime.find_function("test_counter_inc");
|
||||
wasm3::function counter_add_fn = runtime.find_function("test_counter_add");
|
||||
|
||||
// call with no arguments and a return value
|
||||
auto value = counter_get_fn.call<int32_t>();
|
||||
std::cout << "counter: " << value << std::endl;
|
||||
|
||||
// call with no arguments and no return value
|
||||
counter_inc_fn.call();
|
||||
value = counter_get_fn.call<int32_t>();
|
||||
std::cout << "counter after increment: " << value << std::endl;
|
||||
|
||||
// call with one argument and no return value
|
||||
counter_add_fn.call(42);
|
||||
value = counter_get_fn.call<int32_t>();
|
||||
std::cout << "counter after adding value: " << value << std::endl;
|
||||
}
|
||||
}
|
||||
catch(wasm3::error &e) {
|
||||
std::cerr << "WASM3 error: " << e.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
NAME=test_prog
|
||||
SRC=$(NAME).c
|
||||
WASM=$(NAME).wasm
|
||||
HEADER=$(NAME).wasm.h
|
||||
EMCC_FLAGS=-s STANDALONE_WASM -s ERROR_ON_UNDEFINED_SYMBOLS=0 -O3
|
||||
|
||||
all: $(HEADER)
|
||||
|
||||
clean:
|
||||
rm -f $(HEADER) $(WASM)
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
$(WASM): $(SRC)
|
||||
emcc $< -o $@ $(EMCC_FLAGS)
|
||||
|
||||
$(HEADER): $(WASM)
|
||||
xxd -i $< >$@
|
|
@ -0,0 +1,41 @@
|
|||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
extern int sum(int, int);
|
||||
extern int ext_memcpy(void*, const void*, size_t);
|
||||
|
||||
int32_t counter = 0;
|
||||
|
||||
#define WASM_EXPORT __attribute__((used)) __attribute__((visibility ("default")))
|
||||
|
||||
int WASM_EXPORT test(int32_t arg1, int32_t arg2)
|
||||
{
|
||||
int x = arg1 + arg2;
|
||||
int y = arg1 - arg2;
|
||||
return sum(x, y) / 2;
|
||||
}
|
||||
|
||||
int64_t WASM_EXPORT test_memcpy(void)
|
||||
{
|
||||
int64_t x = 0;
|
||||
int32_t low = 0x01234567;
|
||||
int32_t high = 0x89abcdef;
|
||||
ext_memcpy(&x, &low, 4);
|
||||
ext_memcpy(((uint8_t*)&x) + 4, &high, 4);
|
||||
return x;
|
||||
}
|
||||
|
||||
int32_t WASM_EXPORT test_counter_get()
|
||||
{
|
||||
return counter;
|
||||
}
|
||||
|
||||
void WASM_EXPORT test_counter_inc()
|
||||
{
|
||||
++counter;
|
||||
}
|
||||
|
||||
void WASM_EXPORT test_counter_add(int32_t inc_value)
|
||||
{
|
||||
counter += inc_value;
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
unsigned char test_prog_wasm[] = {
|
||||
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x22, 0x07, 0x60,
|
||||
0x02, 0x7f, 0x7f, 0x01, 0x7f, 0x60, 0x01, 0x7f, 0x00, 0x60, 0x00, 0x00,
|
||||
0x60, 0x00, 0x01, 0x7f, 0x60, 0x03, 0x7f, 0x7f, 0x7f, 0x01, 0x7f, 0x60,
|
||||
0x00, 0x01, 0x7e, 0x60, 0x01, 0x7f, 0x01, 0x7f, 0x02, 0x94, 0x01, 0x06,
|
||||
0x03, 0x65, 0x6e, 0x76, 0x03, 0x73, 0x75, 0x6d, 0x00, 0x00, 0x03, 0x65,
|
||||
0x6e, 0x76, 0x0a, 0x65, 0x78, 0x74, 0x5f, 0x6d, 0x65, 0x6d, 0x63, 0x70,
|
||||
0x79, 0x00, 0x04, 0x16, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, 0x6e, 0x61,
|
||||
0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65,
|
||||
0x77, 0x31, 0x0e, 0x61, 0x72, 0x67, 0x73, 0x5f, 0x73, 0x69, 0x7a, 0x65,
|
||||
0x73, 0x5f, 0x67, 0x65, 0x74, 0x00, 0x00, 0x16, 0x77, 0x61, 0x73, 0x69,
|
||||
0x5f, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72,
|
||||
0x65, 0x76, 0x69, 0x65, 0x77, 0x31, 0x08, 0x61, 0x72, 0x67, 0x73, 0x5f,
|
||||
0x67, 0x65, 0x74, 0x00, 0x00, 0x03, 0x65, 0x6e, 0x76, 0x04, 0x6d, 0x61,
|
||||
0x69, 0x6e, 0x00, 0x00, 0x16, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, 0x6e,
|
||||
0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69,
|
||||
0x65, 0x77, 0x31, 0x09, 0x70, 0x72, 0x6f, 0x63, 0x5f, 0x65, 0x78, 0x69,
|
||||
0x74, 0x00, 0x01, 0x03, 0x0c, 0x0b, 0x02, 0x00, 0x05, 0x03, 0x02, 0x01,
|
||||
0x02, 0x03, 0x01, 0x06, 0x03, 0x04, 0x05, 0x01, 0x70, 0x01, 0x02, 0x02,
|
||||
0x05, 0x06, 0x01, 0x01, 0x80, 0x02, 0x80, 0x02, 0x06, 0x09, 0x01, 0x7f,
|
||||
0x01, 0x41, 0x90, 0x88, 0xc0, 0x02, 0x0b, 0x07, 0xb8, 0x01, 0x0c, 0x06,
|
||||
0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00, 0x04, 0x74, 0x65, 0x73,
|
||||
0x74, 0x00, 0x07, 0x0b, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x6d, 0x65, 0x6d,
|
||||
0x63, 0x70, 0x79, 0x00, 0x08, 0x10, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x63,
|
||||
0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x5f, 0x67, 0x65, 0x74, 0x00, 0x09,
|
||||
0x10, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65,
|
||||
0x72, 0x5f, 0x69, 0x6e, 0x63, 0x00, 0x0a, 0x10, 0x74, 0x65, 0x73, 0x74,
|
||||
0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x64,
|
||||
0x00, 0x0b, 0x19, 0x5f, 0x5f, 0x69, 0x6e, 0x64, 0x69, 0x72, 0x65, 0x63,
|
||||
0x74, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74,
|
||||
0x61, 0x62, 0x6c, 0x65, 0x01, 0x00, 0x06, 0x5f, 0x73, 0x74, 0x61, 0x72,
|
||||
0x74, 0x00, 0x0c, 0x10, 0x5f, 0x5f, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x5f,
|
||||
0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x10, 0x09, 0x73,
|
||||
0x74, 0x61, 0x63, 0x6b, 0x53, 0x61, 0x76, 0x65, 0x00, 0x0d, 0x0c, 0x73,
|
||||
0x74, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x00,
|
||||
0x0e, 0x0a, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x41, 0x6c, 0x6c, 0x6f, 0x63,
|
||||
0x00, 0x0f, 0x09, 0x07, 0x01, 0x00, 0x41, 0x01, 0x0b, 0x01, 0x06, 0x0a,
|
||||
0xd1, 0x02, 0x0b, 0x03, 0x00, 0x01, 0x0b, 0x11, 0x00, 0x20, 0x00, 0x20,
|
||||
0x01, 0x6a, 0x20, 0x00, 0x20, 0x01, 0x6b, 0x10, 0x00, 0x41, 0x02, 0x6d,
|
||||
0x0b, 0x58, 0x02, 0x02, 0x7f, 0x01, 0x7e, 0x23, 0x00, 0x41, 0x10, 0x6b,
|
||||
0x22, 0x00, 0x24, 0x00, 0x20, 0x00, 0x42, 0x00, 0x37, 0x03, 0x08, 0x20,
|
||||
0x00, 0x41, 0xe7, 0x8a, 0x8d, 0x09, 0x36, 0x02, 0x04, 0x20, 0x00, 0x41,
|
||||
0xef, 0x9b, 0xaf, 0xcd, 0x78, 0x36, 0x02, 0x00, 0x20, 0x00, 0x41, 0x08,
|
||||
0x6a, 0x22, 0x01, 0x20, 0x00, 0x41, 0x04, 0x6a, 0x41, 0x04, 0x10, 0x01,
|
||||
0x1a, 0x20, 0x01, 0x41, 0x04, 0x72, 0x20, 0x00, 0x41, 0x04, 0x10, 0x01,
|
||||
0x1a, 0x20, 0x00, 0x29, 0x03, 0x08, 0x21, 0x02, 0x20, 0x00, 0x41, 0x10,
|
||||
0x6a, 0x24, 0x00, 0x20, 0x02, 0x0b, 0x08, 0x00, 0x41, 0x80, 0x08, 0x28,
|
||||
0x02, 0x00, 0x0b, 0x11, 0x00, 0x41, 0x80, 0x08, 0x41, 0x80, 0x08, 0x28,
|
||||
0x02, 0x00, 0x41, 0x01, 0x6a, 0x36, 0x02, 0x00, 0x0b, 0x11, 0x00, 0x41,
|
||||
0x80, 0x08, 0x41, 0x80, 0x08, 0x28, 0x02, 0x00, 0x20, 0x00, 0x6a, 0x36,
|
||||
0x02, 0x00, 0x0b, 0x8f, 0x01, 0x01, 0x04, 0x7f, 0x02, 0x7f, 0x23, 0x00,
|
||||
0x41, 0x10, 0x6b, 0x22, 0x00, 0x24, 0x00, 0x02, 0x40, 0x20, 0x00, 0x22,
|
||||
0x01, 0x41, 0x0c, 0x6a, 0x20, 0x00, 0x41, 0x08, 0x6a, 0x10, 0x02, 0x45,
|
||||
0x04, 0x40, 0x20, 0x01, 0x28, 0x02, 0x0c, 0x22, 0x02, 0x04, 0x7f, 0x20,
|
||||
0x00, 0x20, 0x02, 0x41, 0x02, 0x74, 0x22, 0x03, 0x41, 0x13, 0x6a, 0x41,
|
||||
0x70, 0x71, 0x6b, 0x22, 0x00, 0x24, 0x00, 0x20, 0x00, 0x20, 0x01, 0x28,
|
||||
0x02, 0x08, 0x41, 0x0f, 0x6a, 0x41, 0x70, 0x71, 0x6b, 0x22, 0x02, 0x24,
|
||||
0x00, 0x20, 0x00, 0x20, 0x03, 0x6a, 0x41, 0x00, 0x36, 0x02, 0x00, 0x20,
|
||||
0x00, 0x20, 0x02, 0x10, 0x03, 0x0d, 0x02, 0x20, 0x01, 0x28, 0x02, 0x0c,
|
||||
0x05, 0x41, 0x00, 0x0b, 0x20, 0x00, 0x10, 0x04, 0x21, 0x00, 0x20, 0x01,
|
||||
0x41, 0x10, 0x6a, 0x24, 0x00, 0x20, 0x00, 0x0c, 0x02, 0x0b, 0x41, 0xc7,
|
||||
0x00, 0x10, 0x05, 0x00, 0x0b, 0x41, 0xc7, 0x00, 0x10, 0x05, 0x00, 0x0b,
|
||||
0x10, 0x05, 0x00, 0x0b, 0x04, 0x00, 0x23, 0x00, 0x0b, 0x06, 0x00, 0x20,
|
||||
0x00, 0x24, 0x00, 0x0b, 0x10, 0x00, 0x23, 0x00, 0x20, 0x00, 0x6b, 0x41,
|
||||
0x70, 0x71, 0x22, 0x00, 0x24, 0x00, 0x20, 0x00, 0x0b, 0x05, 0x00, 0x41,
|
||||
0x84, 0x08, 0x0b
|
||||
};
|
||||
unsigned int test_prog_wasm_len = 771;
|
|
@ -0,0 +1,4 @@
|
|||
cmake_minimum_required(VERSION 3.5)
|
||||
add_library(wasm3_cpp INTERFACE)
|
||||
target_include_directories(wasm3_cpp INTERFACE include)
|
||||
target_compile_features(wasm3_cpp INTERFACE cxx_std_17)
|
|
@ -0,0 +1,412 @@
|
|||
#pragma once
|
||||
|
||||
#include <tuple>
|
||||
#include <algorithm>
|
||||
#include <type_traits>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <iterator>
|
||||
#include <cassert>
|
||||
|
||||
#include "wasm3.h"
|
||||
|
||||
|
||||
namespace wasm3 {
|
||||
/** @cond */
|
||||
namespace detail {
|
||||
typedef uint64_t *stack_type;
|
||||
typedef void *mem_type;
|
||||
template<typename T, typename...> struct first_type { typedef T type; };
|
||||
|
||||
typedef const void *(*m3_api_raw_fn)(IM3Runtime, uint64_t *, void *);
|
||||
|
||||
template<typename T>
|
||||
void arg_from_stack(T &dest, stack_type &_sp, mem_type mem) {
|
||||
m3ApiGetArg(T, tmp);
|
||||
dest = tmp;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void arg_from_stack(T* &dest, stack_type &_sp, mem_type _mem) {
|
||||
m3ApiGetArgMem(T*, tmp);
|
||||
dest = tmp;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
void arg_from_stack(const T* &dest, stack_type &_sp, mem_type _mem) {
|
||||
m3ApiGetArgMem(const T*, tmp);
|
||||
dest = tmp;
|
||||
};
|
||||
|
||||
template<char c>
|
||||
struct m3_sig {
|
||||
static const char value = c;
|
||||
};
|
||||
template<typename T> struct m3_type_to_sig;
|
||||
template<> struct m3_type_to_sig<int32_t> : m3_sig<'i'> {};
|
||||
template<> struct m3_type_to_sig<int64_t> : m3_sig<'I'> {};
|
||||
template<> struct m3_type_to_sig<float> : m3_sig<'f'> {};
|
||||
template<> struct m3_type_to_sig<double> : m3_sig<'F'> {};
|
||||
template<> struct m3_type_to_sig<void> : m3_sig<'v'> {};
|
||||
template<> struct m3_type_to_sig<void *> : m3_sig<'*'> {};
|
||||
template<> struct m3_type_to_sig<const void *> : m3_sig<'*'> {};
|
||||
|
||||
|
||||
template<typename Ret, typename ... Args>
|
||||
struct m3_signature {
|
||||
constexpr static size_t n_args = sizeof...(Args);
|
||||
constexpr static const char value[n_args + 4] = {
|
||||
m3_type_to_sig<Ret>::value,
|
||||
'(',
|
||||
m3_type_to_sig<Args>::value...,
|
||||
')',
|
||||
0
|
||||
};
|
||||
};
|
||||
|
||||
template <typename ...Args>
|
||||
static void get_args_from_stack(stack_type &sp, mem_type mem, std::tuple<Args...> &tuple) {
|
||||
std::apply([&](auto &... item) {
|
||||
(arg_from_stack(item, sp, mem), ...);
|
||||
}, tuple);
|
||||
}
|
||||
|
||||
template<typename Func>
|
||||
struct wrap_helper;
|
||||
|
||||
template <typename Ret, typename ...Args>
|
||||
struct wrap_helper<Ret(Args...)> {
|
||||
using Func = Ret(Args...);
|
||||
static const void *wrap_fn(IM3Runtime rt, IM3ImportContext _ctx, stack_type _sp, mem_type mem) {
|
||||
std::tuple<Args...> args;
|
||||
// The order here matters: m3ApiReturnType should go before calling get_args_from_stack,
|
||||
// since both modify `_sp`, and the return value on the stack is reserved before the arguments.
|
||||
m3ApiReturnType(Ret);
|
||||
get_args_from_stack(_sp, mem, args);
|
||||
Func* function = reinterpret_cast<Func*>(_ctx->userdata);
|
||||
Ret r = std::apply(function, args);
|
||||
m3ApiReturn(r);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ...Args>
|
||||
struct wrap_helper<void(Args...)> {
|
||||
using Func = void(Args...);
|
||||
static const void *wrap_fn(IM3Runtime rt, IM3ImportContext _ctx, stack_type sp, mem_type mem) {
|
||||
std::tuple<Args...> args;
|
||||
get_args_from_stack(sp, mem, args);
|
||||
Func* function = reinterpret_cast<Func*>(_ctx->userdata);
|
||||
std::apply(function, args);
|
||||
m3ApiSuccess();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Func>
|
||||
class m3_wrapper;
|
||||
|
||||
template<typename Ret, typename ... Args>
|
||||
class m3_wrapper<Ret(Args...)> {
|
||||
public:
|
||||
static M3Result link(IM3Module io_module,
|
||||
const char *const i_moduleName,
|
||||
const char *const i_functionName,
|
||||
Ret (*function)(Args...)) {
|
||||
|
||||
return m3_LinkRawFunctionEx(io_module, i_moduleName, i_functionName,
|
||||
m3_signature<Ret, Args...>::value,
|
||||
&wrap_helper<Ret(Args...)>::wrap_fn,
|
||||
reinterpret_cast<void*>(function));
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
/** @endcond */
|
||||
|
||||
class module;
|
||||
class runtime;
|
||||
class function;
|
||||
|
||||
/**
|
||||
* Exception thrown for wasm3 errors.
|
||||
*
|
||||
* Use error:what() to get the reason as a string.
|
||||
*/
|
||||
class error : public std::runtime_error {
|
||||
public:
|
||||
explicit error(M3Result err) : std::runtime_error(err) {}
|
||||
};
|
||||
|
||||
/** @cond */
|
||||
namespace detail {
|
||||
static inline void check_error(M3Result err) {
|
||||
if (err != m3Err_none) {
|
||||
throw error(err);
|
||||
}
|
||||
}
|
||||
} // namespace detail
|
||||
/** @endcond */
|
||||
|
||||
|
||||
/**
|
||||
* Wrapper for WASM3 environment.
|
||||
*
|
||||
* Runtimes, modules are owned by an environment.
|
||||
*/
|
||||
class environment {
|
||||
public:
|
||||
environment() {
|
||||
m_env.reset(m3_NewEnvironment(), m3_FreeEnvironment);
|
||||
if (m_env == nullptr) {
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new runtime
|
||||
*
|
||||
* @param stack_size_bytes size of the WASM stack for this runtime
|
||||
* @return runtime object
|
||||
*/
|
||||
runtime new_runtime(size_t stack_size_bytes);
|
||||
|
||||
/**
|
||||
* Parse a WASM module from file
|
||||
*
|
||||
* The parsed module is not loaded into any runtime. Use runtime::load to
|
||||
* load the module after parsing it.
|
||||
*
|
||||
* @param in file (WASM binary)
|
||||
* @return module object
|
||||
*/
|
||||
module parse_module(std::istream &in);
|
||||
|
||||
/**
|
||||
* Parse a WASM module from binary data
|
||||
*
|
||||
* @param data pointer to the start of the binary
|
||||
* @param size size of the binary
|
||||
* @return module object
|
||||
*/
|
||||
module parse_module(const uint8_t *data, size_t size);
|
||||
|
||||
protected:
|
||||
std::shared_ptr<struct M3Environment> m_env;
|
||||
};
|
||||
|
||||
/**
|
||||
* Wrapper for the runtime, where modules are loaded and executed.
|
||||
*/
|
||||
class runtime {
|
||||
public:
|
||||
/**
|
||||
* Load the module into runtime
|
||||
* @param mod module parsed by environment::parse_module
|
||||
*/
|
||||
void load(module &mod);
|
||||
|
||||
/**
|
||||
* Get a function handle by name
|
||||
*
|
||||
* If the function is not found, throws an exception.
|
||||
* @param name name of a function, c-string
|
||||
* @return function object
|
||||
*/
|
||||
function find_function(const char *name);
|
||||
|
||||
protected:
|
||||
friend class environment;
|
||||
|
||||
runtime(const std::shared_ptr<M3Environment> &env, size_t stack_size_bytes)
|
||||
: m_env(env) {
|
||||
m_runtime.reset(m3_NewRuntime(env.get(), stack_size_bytes, nullptr), &m3_FreeRuntime);
|
||||
if (m_runtime == nullptr) {
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
}
|
||||
|
||||
/* runtime extends the lifetime of the environment */
|
||||
std::shared_ptr<M3Environment> m_env;
|
||||
std::shared_ptr<M3Runtime> m_runtime;
|
||||
};
|
||||
|
||||
/**
|
||||
* Module object holds a webassembly module
|
||||
*
|
||||
* It can be constructed by parsing a WASM binary using environment::parse_module.
|
||||
* Functions can be linked to the loaded module.
|
||||
* Once constructed, modules can be loaded into the runtime.
|
||||
*/
|
||||
class module {
|
||||
public:
|
||||
/**
|
||||
* Link an external function.
|
||||
*
|
||||
* Throws an exception if the module doesn't reference a function with the given name.
|
||||
*
|
||||
* @tparam Func Function type (signature)
|
||||
* @param module Name of the module to link the function to, or "*" to link to any module
|
||||
* @param function_name Name of the function (as referenced by the module)
|
||||
* @param function Function to link (a function pointer)
|
||||
*/
|
||||
template<typename Func>
|
||||
void link(const char *module, const char *function_name, Func *function);
|
||||
|
||||
/**
|
||||
* Same as module::link, but doesn't throw an exception if the function is not referenced.
|
||||
*/
|
||||
template<typename Func>
|
||||
void link_optional(const char *module, const char *function_name, Func *function);
|
||||
|
||||
|
||||
protected:
|
||||
friend class environment;
|
||||
friend class runtime;
|
||||
|
||||
module(const std::shared_ptr<M3Environment> &env, std::istream &in_wasm) {
|
||||
in_wasm.unsetf(std::ios::skipws);
|
||||
std::copy(std::istream_iterator<uint8_t>(in_wasm),
|
||||
std::istream_iterator<uint8_t>(),
|
||||
std::back_inserter(m_moduleRawData));
|
||||
parse(env.get(), m_moduleRawData.data(), m_moduleRawData.size());
|
||||
}
|
||||
|
||||
module(const std::shared_ptr<M3Environment> &env, const uint8_t *data, size_t size) : m_env(env) {
|
||||
m_moduleRawData = std::vector<uint8_t>{data, data + size};
|
||||
parse(env.get(), m_moduleRawData.data(), m_moduleRawData.size());
|
||||
}
|
||||
|
||||
void parse(IM3Environment env, const uint8_t *data, size_t size) {
|
||||
IM3Module p;
|
||||
M3Result err = m3_ParseModule(env, &p, data, size);
|
||||
detail::check_error(err);
|
||||
m_module.reset(p, [this](IM3Module module) {
|
||||
if (!m_loaded) {
|
||||
m3_FreeModule(module);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void load_into(IM3Runtime runtime) {
|
||||
M3Result err = m3_LoadModule(runtime, m_module.get());
|
||||
detail::check_error(err);
|
||||
m_loaded = true;
|
||||
}
|
||||
|
||||
std::shared_ptr<M3Environment> m_env;
|
||||
std::shared_ptr<M3Module> m_module;
|
||||
bool m_loaded = false;
|
||||
std::vector<uint8_t> m_moduleRawData {};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handle of a function. Can be obtained from runtime::find_function method by name.
|
||||
*/
|
||||
class function {
|
||||
public:
|
||||
/**
|
||||
* Call the function with the provided arguments, expressed as strings.
|
||||
*
|
||||
* Arguments are passed as strings. WASM3 automatically converts them into the types expected
|
||||
* by the function being called.
|
||||
*
|
||||
* Note that the type of the return value must be explicitly specified as a template argument.
|
||||
*
|
||||
* @return the return value of the function.
|
||||
*/
|
||||
template<typename Ret, typename ... Args>
|
||||
typename detail::first_type<Ret,
|
||||
typename std::enable_if<std::is_convertible<Args, const char*>::value>::type...>::type
|
||||
call_argv(Args... args) {
|
||||
/* std::enable_if above checks that all argument types are convertible const char* */
|
||||
const char* argv[] = {args...};
|
||||
M3Result res = m3_CallArgv(m_func, sizeof...(args), argv);
|
||||
detail::check_error(res);
|
||||
Ret ret;
|
||||
res = m3_GetResults(m_func, 1, &ret);
|
||||
detail::check_error(res);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename ... Args>
|
||||
typename detail::first_type<void,
|
||||
typename std::enable_if<std::is_convertible<Args, const char*>::value>::type...>::type
|
||||
call_argv(Args... args) {
|
||||
/* std::enable_if above checks that all argument types are convertible const char* */
|
||||
const char* argv[] = {args...};
|
||||
M3Result res = m3_CallArgv(m_func, sizeof...(args), argv);
|
||||
detail::check_error(res);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the function with the provided arguments (int/float types).
|
||||
*
|
||||
* Note that the type of the return value must be explicitly specified as a template argument.
|
||||
*
|
||||
* @return the return value of the function or void.
|
||||
*/
|
||||
template<typename Ret = void, typename ... Args>
|
||||
Ret call(Args... args) {
|
||||
const void *arg_ptrs[] = { reinterpret_cast<const void*>(&args)... };
|
||||
M3Result res = m3_Call(m_func, sizeof...(args), arg_ptrs);
|
||||
detail::check_error(res);
|
||||
|
||||
if constexpr (!std::is_void<Ret>::value) {
|
||||
Ret ret;
|
||||
const void* ret_ptrs[] = { &ret };
|
||||
res = m3_GetResults(m_func, 1, ret_ptrs);
|
||||
detail::check_error(res);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
friend class runtime;
|
||||
|
||||
function(const std::shared_ptr<M3Runtime> &runtime, const char *name) : m_runtime(runtime) {
|
||||
M3Result err = m3_FindFunction(&m_func, runtime.get(), name);
|
||||
detail::check_error(err);
|
||||
assert(m_func != nullptr);
|
||||
}
|
||||
|
||||
std::shared_ptr<M3Runtime> m_runtime;
|
||||
M3Function *m_func = nullptr;
|
||||
};
|
||||
|
||||
inline runtime environment::new_runtime(size_t stack_size_bytes) {
|
||||
return runtime(m_env, stack_size_bytes);
|
||||
}
|
||||
|
||||
inline module environment::parse_module(std::istream &in) {
|
||||
return module(m_env, in);
|
||||
}
|
||||
|
||||
inline module environment::parse_module(const uint8_t *data, size_t size) {
|
||||
return module(m_env, data, size);
|
||||
}
|
||||
|
||||
inline void runtime::load(module &mod) {
|
||||
mod.load_into(m_runtime.get());
|
||||
}
|
||||
|
||||
inline function runtime::find_function(const char *name) {
|
||||
return function(m_runtime, name);
|
||||
}
|
||||
|
||||
template<typename Func>
|
||||
void module::link(const char *module, const char *function_name, Func *function) {
|
||||
M3Result ret = detail::m3_wrapper<Func>::link(m_module.get(), module, function_name, function);
|
||||
detail::check_error(ret);
|
||||
}
|
||||
|
||||
template<typename Func>
|
||||
void module::link_optional(const char *module, const char *function_name, Func *function) {
|
||||
M3Result ret = detail::m3_wrapper<Func>::link(m_module.get(), module, function_name, function);
|
||||
if (ret == m3Err_functionLookupFailed) {
|
||||
return;
|
||||
}
|
||||
detail::check_error(ret);
|
||||
}
|
||||
|
||||
} // namespace wasm3
|
|
@ -0,0 +1 @@
|
|||
.pio
|
|
@ -0,0 +1 @@
|
|||
../../../../source
|
|
@ -0,0 +1,137 @@
|
|||
; PlatformIO Project Configuration File
|
||||
;
|
||||
; Build options: build flags, source filter
|
||||
; Upload options: custom upload port, speed and extra flags
|
||||
; Library options: dependencies, extra library storages
|
||||
; Advanced options: extra scripting
|
||||
;
|
||||
; Please visit documentation for the other options and examples
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[platformio]
|
||||
default_envs =
|
||||
mkr1000
|
||||
due
|
||||
mega1284
|
||||
tinyBLE
|
||||
blenano
|
||||
blenano2
|
||||
teensy31
|
||||
bluepill-mapple
|
||||
bluepill
|
||||
az3166
|
||||
maix
|
||||
#titiva TODO: undefined reference to `_exit'
|
||||
|
||||
[env]
|
||||
framework = arduino
|
||||
monitor_speed = 115200
|
||||
|
||||
src_filter =
|
||||
+<*>
|
||||
-<m3/extensions/*>
|
||||
|
||||
[env:mkr1000]
|
||||
platform = atmelsam
|
||||
board = mkr1000USB
|
||||
|
||||
src_build_flags =
|
||||
-O3 -Wfatal-errors
|
||||
-flto
|
||||
#-fPIC -fomit-frame-pointer -foptimize-sibling-calls
|
||||
|
||||
[env:due]
|
||||
platform = atmelsam
|
||||
board = due
|
||||
|
||||
src_build_flags =
|
||||
-O3 -Wfatal-errors
|
||||
-flto
|
||||
|
||||
[env:mega1284]
|
||||
platform = atmelavr
|
||||
board = wildfirev3
|
||||
|
||||
src_build_flags =
|
||||
-Dd_m3CodePageAlignSize=512
|
||||
-Os -Wfatal-errors
|
||||
-flto
|
||||
|
||||
[env:tinyBLE]
|
||||
platform = nordicnrf51
|
||||
board = seeedTinyBLE
|
||||
|
||||
src_build_flags =
|
||||
-O3 -Wfatal-errors
|
||||
-flto
|
||||
|
||||
[env:blenano]
|
||||
platform = nordicnrf51
|
||||
board = redBearLabBLENano
|
||||
|
||||
src_build_flags =
|
||||
-O3 -Wfatal-errors
|
||||
-flto
|
||||
|
||||
[env:blenano2]
|
||||
platform = nordicnrf52
|
||||
board = redbear_blenano2
|
||||
|
||||
src_build_flags =
|
||||
-O3 -Wfatal-errors
|
||||
-flto
|
||||
|
||||
[env:teensy31]
|
||||
platform = teensy
|
||||
board = teensy31
|
||||
upload_protocol = teensy-cli
|
||||
|
||||
src_build_flags =
|
||||
-O3 -Wfatal-errors
|
||||
-flto
|
||||
|
||||
[env:bluepill-mapple]
|
||||
platform = ststm32
|
||||
board = bluepill_f103c8_128k
|
||||
board_build.core = maple
|
||||
upload_protocol = stlink
|
||||
#upload_protocol = dfu
|
||||
|
||||
src_build_flags =
|
||||
-Dd_m3FixedHeap=8192
|
||||
-Os -Wfatal-errors
|
||||
-flto
|
||||
|
||||
[env:bluepill]
|
||||
platform = ststm32
|
||||
board = bluepill_f103c8
|
||||
upload_protocol = stlink
|
||||
|
||||
src_build_flags =
|
||||
-Dd_m3FixedHeap=8192
|
||||
-Os -Wfatal-errors
|
||||
-flto
|
||||
|
||||
[env:az3166]
|
||||
platform = ststm32
|
||||
board = mxchip_az3166
|
||||
|
||||
src_build_flags =
|
||||
-O3 -Wfatal-errors
|
||||
-flto
|
||||
|
||||
[env:maix]
|
||||
platform = kendryte210
|
||||
board = sipeed-maix-one-dock
|
||||
|
||||
src_build_flags =
|
||||
-O3 -Wfatal-errors
|
||||
-flto
|
||||
|
||||
[env:titiva]
|
||||
platform = titiva
|
||||
board = lptm4c1294ncpdt
|
||||
|
||||
src_build_flags =
|
||||
-DLED_BUILTIN=13
|
||||
-O3 -Wfatal-errors
|
|
@ -0,0 +1,82 @@
|
|||
//
|
||||
// Wasm3 - high performance WebAssembly interpreter written in C.
|
||||
//
|
||||
// Copyright © 2019 Steven Massey, Volodymyr Shymanskyy.
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
#include "wasm3.h"
|
||||
|
||||
#include "extra/fib32.wasm.h"
|
||||
|
||||
#define FATAL(func, msg) { \
|
||||
Serial.print("Fatal: " func ": "); \
|
||||
Serial.println(msg); return; }
|
||||
|
||||
void run_wasm()
|
||||
{
|
||||
M3Result result = m3Err_none;
|
||||
|
||||
uint8_t* wasm = (uint8_t*)fib32_wasm;
|
||||
size_t fsize = fib32_wasm_len;
|
||||
|
||||
Serial.println("Loading WebAssembly...");
|
||||
|
||||
IM3Environment env = m3_NewEnvironment ();
|
||||
if (!env) FATAL("m3_NewEnvironment", "failed");
|
||||
|
||||
IM3Runtime runtime = m3_NewRuntime (env, 1024, NULL);
|
||||
if (!runtime) FATAL("m3_NewRuntime", "failed");
|
||||
|
||||
IM3Module module;
|
||||
result = m3_ParseModule (env, &module, wasm, fsize);
|
||||
if (result) FATAL("m3_ParseModule", result);
|
||||
|
||||
result = m3_LoadModule (runtime, module);
|
||||
if (result) FATAL("m3_LoadModule", result);
|
||||
|
||||
IM3Function f;
|
||||
result = m3_FindFunction (&f, runtime, "fib");
|
||||
if (result) FATAL("m3_FindFunction", result);
|
||||
|
||||
Serial.println("Running...");
|
||||
|
||||
result = m3_CallV(f, 24);
|
||||
if (result) FATAL("m3_Call", result);
|
||||
|
||||
uint32_t value = 0;
|
||||
result = m3_GetResultsV (f, &value);
|
||||
if (result) FATAL("m3_GetResults: %s", result);
|
||||
|
||||
Serial.print("Result: ");
|
||||
Serial.println(value);
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
|
||||
Serial.begin(115200);
|
||||
delay(10);
|
||||
while (!Serial) {}
|
||||
|
||||
Serial.println();
|
||||
Serial.println("Wasm3 v" M3_VERSION " on Arduino (" M3_ARCH "), build " __DATE__ " " __TIME__);
|
||||
|
||||
digitalWrite(LED_BUILTIN, HIGH);
|
||||
uint32_t start = millis();
|
||||
run_wasm();
|
||||
uint32_t end = millis();
|
||||
digitalWrite(LED_BUILTIN, LOW);
|
||||
|
||||
Serial.print("Elapsed: ");
|
||||
Serial.print(end - start);
|
||||
Serial.println(" ms");
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
delay(100);
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
.pio
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"build" : {
|
||||
"flags": "-Os -fomit-frame-pointer -fno-stack-check -fno-stack-protector -Wfatal-errors -Wno-unused-function -Wno-unused-variable -Wno-unused-parameter -Wno-missing-field-initializers",
|
||||
"srcFilter": ["+<*>", "-<extensions/*>"],
|
||||
"libArchive": false
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
../../../../../source
|
|
@ -0,0 +1,10 @@
|
|||
[env:bluepill]
|
||||
platform = ststm32
|
||||
board = bluepill_f103c8
|
||||
framework = stm32cube
|
||||
upload_protocol = stlink
|
||||
lib_deps = jeeh
|
||||
|
||||
build_flags =
|
||||
-Dd_m3FixedHeap=8192
|
||||
-Os -Wfatal-errors
|
|
@ -0,0 +1,75 @@
|
|||
//
|
||||
// Wasm3 - high performance WebAssembly interpreter written in C.
|
||||
//
|
||||
// Copyright © 2019 Steven Massey, Volodymyr Shymanskyy.
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
#include "wasm3.h"
|
||||
|
||||
#include "extra/fib32.wasm.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <jee.h>
|
||||
|
||||
#define FATAL(func, msg) { \
|
||||
puts("Fatal: " func ": "); \
|
||||
puts(msg); return; }
|
||||
|
||||
void run_wasm()
|
||||
{
|
||||
M3Result result = m3Err_none;
|
||||
|
||||
uint8_t* wasm = (uint8_t*)fib32_wasm;
|
||||
size_t fsize = fib32_wasm_len;
|
||||
|
||||
puts("Loading WebAssembly...");
|
||||
|
||||
IM3Environment env = m3_NewEnvironment ();
|
||||
if (!env) FATAL("m3_NewEnvironment", "failed");
|
||||
|
||||
IM3Runtime runtime = m3_NewRuntime (env, 1024, NULL);
|
||||
if (!runtime) FATAL("m3_NewRuntime", "failed");
|
||||
|
||||
IM3Module module;
|
||||
result = m3_ParseModule (env, &module, wasm, fsize);
|
||||
if (result) FATAL("m3_ParseModule", result);
|
||||
|
||||
result = m3_LoadModule (runtime, module);
|
||||
if (result) FATAL("m3_LoadModule", result);
|
||||
|
||||
IM3Function f;
|
||||
result = m3_FindFunction (&f, runtime, "fib");
|
||||
if (result) FATAL("m3_FindFunction", result);
|
||||
|
||||
puts("Running...");
|
||||
|
||||
result = m3_CallV (f, 24);
|
||||
if (result) FATAL("m3_Call", result);
|
||||
|
||||
uint32_t value = 0;
|
||||
result = m3_GetResultsV (f, &value);
|
||||
if (result) FATAL("m3_GetResults", result);
|
||||
|
||||
char buff[32];
|
||||
itoa(value, buff, 10);
|
||||
|
||||
puts("Result: ");
|
||||
puts(buff);
|
||||
puts("\n");
|
||||
}
|
||||
|
||||
PinC<13> led;
|
||||
|
||||
int main()
|
||||
{
|
||||
enableSysTick();
|
||||
led.mode(Pinmode::out);
|
||||
|
||||
puts("Wasm3 v" M3_VERSION " on BluePill, build " __DATE__ " " __TIME__ "\n");
|
||||
|
||||
led = 0;
|
||||
run_wasm();
|
||||
led = 1;
|
||||
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
build
|
||||
sdkconfig
|
||||
sdkconfig.old
|
|
@ -0,0 +1,9 @@
|
|||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := wasm3
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
#
|
||||
# "main" pseudo-component makefile.
|
||||
#
|
||||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
# The following lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(wasm3)
|
|
@ -0,0 +1,24 @@
|
|||
## Build for ESP-IDF, with minimal WASI support
|
||||
|
||||
**Note:** Currently, to run this example you need a PSRAM-enabled ESP32 module (this might be improved in future).
|
||||
|
||||
Download and install ESP-IDF v4.0
|
||||
|
||||
```sh
|
||||
export IDF_PATH=/opt/esp32/esp-idf
|
||||
|
||||
# Set tools path if needed:
|
||||
#export IDF_TOOLS_PATH=/opt/esp32
|
||||
|
||||
source $IDF_PATH/export.sh
|
||||
|
||||
idf.py menuconfig
|
||||
|
||||
# Select target:
|
||||
idf.py set-target esp32 # or: esp32s2, esp32c3, esp32s3, linux
|
||||
|
||||
idf.py build
|
||||
|
||||
idf.py -p /dev/ttyUSB0 flash monitor
|
||||
|
||||
```
|
|
@ -0,0 +1,11 @@
|
|||
file(GLOB_RECURSE M3_SOURCES "wasm3/*.c")
|
||||
|
||||
set(APP_SOURCES "main.cpp" "m3_api_esp_wasi.c")
|
||||
|
||||
idf_component_register(SRCS ${APP_SOURCES} ${M3_SOURCES}
|
||||
INCLUDE_DIRS "wasm3")
|
||||
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-error -O3 -DESP32 -Dd_m3MaxFunctionStackHeight=256)
|
||||
|
||||
# Disable harmless warnings
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-unused-function -Wno-unused-variable -Wno-unused-parameter -Wno-missing-field-initializers)
|
|
@ -0,0 +1,616 @@
|
|||
//
|
||||
// m3_api_esp_wasi.c
|
||||
//
|
||||
// Created by Volodymyr Shymanskyy on 01/07/20.
|
||||
// Copyright © 2019 Volodymyr Shymanskyy. All rights reserved.
|
||||
//
|
||||
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
|
||||
#include "m3_api_esp_wasi.h"
|
||||
|
||||
#include "m3_env.h"
|
||||
#include "m3_exception.h"
|
||||
|
||||
#if defined(ESP32)
|
||||
|
||||
typedef uint32_t __wasi_size_t;
|
||||
#include "extra/wasi_core.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static m3_wasi_context_t* wasi_context;
|
||||
|
||||
typedef struct wasi_iovec_t
|
||||
{
|
||||
__wasi_size_t buf;
|
||||
__wasi_size_t buf_len;
|
||||
} wasi_iovec_t;
|
||||
|
||||
#define PREOPEN_CNT 3
|
||||
|
||||
typedef struct Preopen {
|
||||
int fd;
|
||||
char* path;
|
||||
} Preopen;
|
||||
|
||||
Preopen preopen[PREOPEN_CNT] = {
|
||||
{ 0, "<stdin>" },
|
||||
{ 1, "<stdout>" },
|
||||
{ 2, "<stderr>" },
|
||||
};
|
||||
|
||||
# define APE_SWITCH_BEG switch (errnum) {
|
||||
# define APE_SWITCH_END }
|
||||
# define APE_CASE_RET(e1,e2) case e1: return e2; break;
|
||||
|
||||
static
|
||||
__wasi_errno_t errno_to_wasi(int errnum) {
|
||||
APE_SWITCH_BEG
|
||||
APE_CASE_RET( EPERM , __WASI_ERRNO_PERM )
|
||||
APE_CASE_RET( ENOENT , __WASI_ERRNO_NOENT )
|
||||
APE_CASE_RET( ESRCH , __WASI_ERRNO_SRCH )
|
||||
APE_CASE_RET( EINTR , __WASI_ERRNO_INTR )
|
||||
APE_CASE_RET( EIO , __WASI_ERRNO_IO )
|
||||
APE_CASE_RET( ENXIO , __WASI_ERRNO_NXIO )
|
||||
APE_CASE_RET( E2BIG , __WASI_ERRNO_2BIG )
|
||||
APE_CASE_RET( ENOEXEC , __WASI_ERRNO_NOEXEC )
|
||||
APE_CASE_RET( EBADF , __WASI_ERRNO_BADF )
|
||||
APE_CASE_RET( ECHILD , __WASI_ERRNO_CHILD )
|
||||
APE_CASE_RET( EAGAIN , __WASI_ERRNO_AGAIN )
|
||||
APE_CASE_RET( ENOMEM , __WASI_ERRNO_NOMEM )
|
||||
APE_CASE_RET( EACCES , __WASI_ERRNO_ACCES )
|
||||
APE_CASE_RET( EFAULT , __WASI_ERRNO_FAULT )
|
||||
APE_CASE_RET( EBUSY , __WASI_ERRNO_BUSY )
|
||||
APE_CASE_RET( EEXIST , __WASI_ERRNO_EXIST )
|
||||
APE_CASE_RET( EXDEV , __WASI_ERRNO_XDEV )
|
||||
APE_CASE_RET( ENODEV , __WASI_ERRNO_NODEV )
|
||||
APE_CASE_RET( ENOTDIR , __WASI_ERRNO_NOTDIR )
|
||||
APE_CASE_RET( EISDIR , __WASI_ERRNO_ISDIR )
|
||||
APE_CASE_RET( EINVAL , __WASI_ERRNO_INVAL )
|
||||
APE_CASE_RET( ENFILE , __WASI_ERRNO_NFILE )
|
||||
APE_CASE_RET( EMFILE , __WASI_ERRNO_MFILE )
|
||||
APE_CASE_RET( ENOTTY , __WASI_ERRNO_NOTTY )
|
||||
APE_CASE_RET( ETXTBSY , __WASI_ERRNO_TXTBSY )
|
||||
APE_CASE_RET( EFBIG , __WASI_ERRNO_FBIG )
|
||||
APE_CASE_RET( ENOSPC , __WASI_ERRNO_NOSPC )
|
||||
APE_CASE_RET( ESPIPE , __WASI_ERRNO_SPIPE )
|
||||
APE_CASE_RET( EROFS , __WASI_ERRNO_ROFS )
|
||||
APE_CASE_RET( EMLINK , __WASI_ERRNO_MLINK )
|
||||
APE_CASE_RET( EPIPE , __WASI_ERRNO_PIPE )
|
||||
APE_CASE_RET( EDOM , __WASI_ERRNO_DOM )
|
||||
APE_CASE_RET( ERANGE , __WASI_ERRNO_RANGE )
|
||||
APE_SWITCH_END
|
||||
return __WASI_ERRNO_INVAL;
|
||||
}
|
||||
|
||||
static inline
|
||||
int convert_clockid(__wasi_clockid_t in) {
|
||||
switch (in) {
|
||||
case __WASI_CLOCKID_MONOTONIC: return CLOCK_MONOTONIC;
|
||||
//case __WASI_CLOCKID_PROCESS_CPUTIME_ID: return CLOCK_PROCESS_CPUTIME_ID;
|
||||
case __WASI_CLOCKID_REALTIME: return CLOCK_REALTIME;
|
||||
//case __WASI_CLOCKID_THREAD_CPUTIME_ID: return CLOCK_THREAD_CPUTIME_ID;
|
||||
default: return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static inline
|
||||
__wasi_timestamp_t convert_timespec(const struct timespec *ts) {
|
||||
if (ts->tv_sec < 0)
|
||||
return 0;
|
||||
if ((__wasi_timestamp_t)ts->tv_sec >= UINT64_MAX / 1000000000)
|
||||
return UINT64_MAX;
|
||||
return (__wasi_timestamp_t)ts->tv_sec * 1000000000 + ts->tv_nsec;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* WASI API implementation
|
||||
*/
|
||||
|
||||
m3ApiRawFunction(m3_wasi_generic_args_get)
|
||||
{
|
||||
m3ApiReturnType (uint32_t)
|
||||
m3ApiGetArgMem (uint32_t * , argv)
|
||||
m3ApiGetArgMem (char * , argv_buf)
|
||||
|
||||
m3_wasi_context_t* context = (m3_wasi_context_t*)(_ctx->userdata);
|
||||
|
||||
if (context == NULL) { m3ApiReturn(__WASI_ERRNO_INVAL); }
|
||||
|
||||
m3ApiCheckMem(argv, context->argc * sizeof(uint32_t));
|
||||
|
||||
for (u32 i = 0; i < context->argc; ++i)
|
||||
{
|
||||
m3ApiWriteMem32(&argv[i], m3ApiPtrToOffset(argv_buf));
|
||||
|
||||
size_t len = strlen (context->argv[i]);
|
||||
|
||||
m3ApiCheckMem(argv_buf, len);
|
||||
memcpy (argv_buf, context->argv[i], len);
|
||||
argv_buf += len;
|
||||
* argv_buf++ = 0;
|
||||
}
|
||||
|
||||
m3ApiReturn(__WASI_ERRNO_SUCCESS);
|
||||
}
|
||||
|
||||
m3ApiRawFunction(m3_wasi_generic_args_sizes_get)
|
||||
{
|
||||
m3ApiReturnType (uint32_t)
|
||||
m3ApiGetArgMem (__wasi_size_t * , argc)
|
||||
m3ApiGetArgMem (__wasi_size_t * , argv_buf_size)
|
||||
|
||||
m3ApiCheckMem(argc, sizeof(__wasi_size_t));
|
||||
m3ApiCheckMem(argv_buf_size, sizeof(__wasi_size_t));
|
||||
|
||||
m3_wasi_context_t* context = (m3_wasi_context_t*)(_ctx->userdata);
|
||||
|
||||
if (context == NULL) { m3ApiReturn(__WASI_ERRNO_INVAL); }
|
||||
|
||||
__wasi_size_t buf_len = 0;
|
||||
for (u32 i = 0; i < context->argc; ++i)
|
||||
{
|
||||
buf_len += strlen (context->argv[i]) + 1;
|
||||
}
|
||||
|
||||
m3ApiWriteMem32(argc, context->argc);
|
||||
m3ApiWriteMem32(argv_buf_size, buf_len);
|
||||
|
||||
m3ApiReturn(__WASI_ERRNO_SUCCESS);
|
||||
}
|
||||
|
||||
m3ApiRawFunction(m3_wasi_generic_environ_get)
|
||||
{
|
||||
m3ApiReturnType (uint32_t)
|
||||
m3ApiGetArgMem (uint32_t * , env)
|
||||
m3ApiGetArgMem (char * , env_buf)
|
||||
|
||||
// TODO
|
||||
m3ApiReturn(__WASI_ERRNO_SUCCESS);
|
||||
}
|
||||
|
||||
m3ApiRawFunction(m3_wasi_generic_environ_sizes_get)
|
||||
{
|
||||
m3ApiReturnType (uint32_t)
|
||||
m3ApiGetArgMem (__wasi_size_t * , env_count)
|
||||
m3ApiGetArgMem (__wasi_size_t * , env_buf_size)
|
||||
|
||||
m3ApiCheckMem(env_count, sizeof(__wasi_size_t));
|
||||
m3ApiCheckMem(env_buf_size, sizeof(__wasi_size_t));
|
||||
|
||||
// TODO
|
||||
m3ApiWriteMem32(env_count, 0);
|
||||
m3ApiWriteMem32(env_buf_size, 0);
|
||||
|
||||
m3ApiReturn(__WASI_ERRNO_SUCCESS);
|
||||
}
|
||||
|
||||
m3ApiRawFunction(m3_wasi_generic_fd_prestat_dir_name)
|
||||
{
|
||||
m3ApiReturnType (uint32_t)
|
||||
m3ApiGetArg (__wasi_fd_t , fd)
|
||||
m3ApiGetArgMem (char * , path)
|
||||
m3ApiGetArg (__wasi_size_t , path_len)
|
||||
|
||||
m3ApiCheckMem(path, path_len);
|
||||
|
||||
if (fd < 3 || fd >= PREOPEN_CNT) { m3ApiReturn(__WASI_ERRNO_BADF); }
|
||||
size_t slen = strlen(preopen[fd].path) + 1;
|
||||
memcpy(path, preopen[fd].path, M3_MIN(slen, path_len));
|
||||
m3ApiReturn(__WASI_ERRNO_SUCCESS);
|
||||
}
|
||||
|
||||
m3ApiRawFunction(m3_wasi_generic_fd_prestat_get)
|
||||
{
|
||||
m3ApiReturnType (uint32_t)
|
||||
m3ApiGetArg (__wasi_fd_t , fd)
|
||||
m3ApiGetArgMem (uint8_t * , buf)
|
||||
|
||||
m3ApiCheckMem(buf, 8);
|
||||
|
||||
if (fd < 3 || fd >= PREOPEN_CNT) { m3ApiReturn(__WASI_ERRNO_BADF); }
|
||||
|
||||
m3ApiWriteMem32(buf+0, __WASI_PREOPENTYPE_DIR);
|
||||
m3ApiWriteMem32(buf+4, strlen(preopen[fd].path) + 1);
|
||||
m3ApiReturn(__WASI_ERRNO_SUCCESS);
|
||||
}
|
||||
|
||||
m3ApiRawFunction(m3_wasi_generic_fd_fdstat_get)
|
||||
{
|
||||
m3ApiReturnType (uint32_t)
|
||||
m3ApiGetArg (__wasi_fd_t , fd)
|
||||
m3ApiGetArgMem (__wasi_fdstat_t * , fdstat)
|
||||
|
||||
m3ApiCheckMem(fdstat, sizeof(__wasi_fdstat_t));
|
||||
|
||||
struct stat fd_stat;
|
||||
int fl = fcntl(fd, F_GETFL);
|
||||
if (fl < 0) { m3ApiReturn(errno_to_wasi(errno)); }
|
||||
fstat(fd, &fd_stat);
|
||||
int mode = fd_stat.st_mode;
|
||||
fdstat->fs_filetype = (S_ISBLK(mode) ? __WASI_FILETYPE_BLOCK_DEVICE : 0) |
|
||||
(S_ISCHR(mode) ? __WASI_FILETYPE_CHARACTER_DEVICE : 0) |
|
||||
(S_ISDIR(mode) ? __WASI_FILETYPE_DIRECTORY : 0) |
|
||||
(S_ISREG(mode) ? __WASI_FILETYPE_REGULAR_FILE : 0) |
|
||||
//(S_ISSOCK(mode) ? __WASI_FILETYPE_SOCKET_STREAM : 0) |
|
||||
(S_ISLNK(mode) ? __WASI_FILETYPE_SYMBOLIC_LINK : 0);
|
||||
m3ApiWriteMem16(&fdstat->fs_flags,
|
||||
((fl & O_APPEND) ? __WASI_FDFLAGS_APPEND : 0) |
|
||||
//((fl & O_DSYNC) ? __WASI_FDFLAGS_DSYNC : 0) |
|
||||
((fl & O_NONBLOCK) ? __WASI_FDFLAGS_NONBLOCK : 0) |
|
||||
//((fl & O_RSYNC) ? __WASI_FDFLAGS_RSYNC : 0) |
|
||||
((fl & O_SYNC) ? __WASI_FDFLAGS_SYNC : 0));
|
||||
|
||||
fdstat->fs_rights_base = (uint64_t)-1; // all rights
|
||||
|
||||
// Make descriptors 0,1,2 look like a TTY
|
||||
if (fd <= 2) {
|
||||
fdstat->fs_rights_base &= ~(__WASI_RIGHTS_FD_SEEK | __WASI_RIGHTS_FD_TELL);
|
||||
}
|
||||
|
||||
fdstat->fs_rights_inheriting = (uint64_t)-1; // all rights
|
||||
m3ApiReturn(__WASI_ERRNO_SUCCESS);
|
||||
}
|
||||
|
||||
m3ApiRawFunction(m3_wasi_generic_fd_fdstat_set_flags)
|
||||
{
|
||||
m3ApiReturnType (uint32_t)
|
||||
m3ApiGetArg (__wasi_fd_t , fd)
|
||||
m3ApiGetArg (__wasi_fdflags_t , flags)
|
||||
|
||||
// TODO
|
||||
|
||||
m3ApiReturn(__WASI_ERRNO_SUCCESS);
|
||||
}
|
||||
|
||||
m3ApiRawFunction(m3_wasi_unstable_fd_seek)
|
||||
{
|
||||
m3ApiReturnType (uint32_t)
|
||||
m3ApiGetArg (__wasi_fd_t , fd)
|
||||
m3ApiGetArg (__wasi_filedelta_t , offset)
|
||||
m3ApiGetArg (uint32_t , wasi_whence)
|
||||
m3ApiGetArgMem (__wasi_filesize_t * , result)
|
||||
|
||||
m3ApiCheckMem(result, sizeof(__wasi_filesize_t));
|
||||
|
||||
int whence;
|
||||
|
||||
switch (wasi_whence) {
|
||||
case 0: whence = SEEK_CUR; break;
|
||||
case 1: whence = SEEK_END; break;
|
||||
case 2: whence = SEEK_SET; break;
|
||||
default: m3ApiReturn(__WASI_ERRNO_INVAL);
|
||||
}
|
||||
|
||||
int64_t ret;
|
||||
ret = lseek(fd, offset, whence);
|
||||
if (ret < 0) { m3ApiReturn(errno_to_wasi(errno)); }
|
||||
m3ApiWriteMem64(result, ret);
|
||||
m3ApiReturn(__WASI_ERRNO_SUCCESS);
|
||||
}
|
||||
|
||||
m3ApiRawFunction(m3_wasi_snapshot_preview1_fd_seek)
|
||||
{
|
||||
m3ApiReturnType (uint32_t)
|
||||
m3ApiGetArg (__wasi_fd_t , fd)
|
||||
m3ApiGetArg (__wasi_filedelta_t , offset)
|
||||
m3ApiGetArg (uint32_t , wasi_whence)
|
||||
m3ApiGetArgMem (__wasi_filesize_t * , result)
|
||||
|
||||
m3ApiCheckMem(result, sizeof(__wasi_filesize_t));
|
||||
|
||||
int whence;
|
||||
|
||||
switch (wasi_whence) {
|
||||
case 0: whence = SEEK_SET; break;
|
||||
case 1: whence = SEEK_CUR; break;
|
||||
case 2: whence = SEEK_END; break;
|
||||
default: m3ApiReturn(__WASI_ERRNO_INVAL);
|
||||
}
|
||||
|
||||
int64_t ret;
|
||||
ret = lseek(fd, offset, whence);
|
||||
if (ret < 0) { m3ApiReturn(errno_to_wasi(errno)); }
|
||||
m3ApiWriteMem64(result, ret);
|
||||
m3ApiReturn(__WASI_ERRNO_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
m3ApiRawFunction(m3_wasi_generic_path_open)
|
||||
{
|
||||
m3ApiReturnType (uint32_t)
|
||||
m3ApiGetArg (__wasi_fd_t , dirfd)
|
||||
m3ApiGetArg (__wasi_lookupflags_t , dirflags)
|
||||
m3ApiGetArgMem (const char * , path)
|
||||
m3ApiGetArg (__wasi_size_t , path_len)
|
||||
m3ApiGetArg (__wasi_oflags_t , oflags)
|
||||
m3ApiGetArg (__wasi_rights_t , fs_rights_base)
|
||||
m3ApiGetArg (__wasi_rights_t , fs_rights_inheriting)
|
||||
m3ApiGetArg (__wasi_fdflags_t , fs_flags)
|
||||
m3ApiGetArgMem (__wasi_fd_t * , fd)
|
||||
|
||||
m3ApiCheckMem(path, path_len);
|
||||
m3ApiCheckMem(fd, sizeof(__wasi_fd_t));
|
||||
|
||||
if (path_len >= 512)
|
||||
m3ApiReturn(__WASI_ERRNO_INVAL);
|
||||
|
||||
// copy path so we can ensure it is NULL terminated
|
||||
char host_path[path_len+1];
|
||||
|
||||
memcpy (host_path, path, path_len);
|
||||
host_path[path_len] = '\0'; // NULL terminator
|
||||
|
||||
// TODO
|
||||
m3ApiReturn(__WASI_ERRNO_NOSYS);
|
||||
}
|
||||
|
||||
m3ApiRawFunction(m3_wasi_generic_fd_read)
|
||||
{
|
||||
m3ApiReturnType (uint32_t)
|
||||
m3ApiGetArg (__wasi_fd_t , fd)
|
||||
m3ApiGetArgMem (wasi_iovec_t * , wasi_iovs)
|
||||
m3ApiGetArg (__wasi_size_t , iovs_len)
|
||||
m3ApiGetArgMem (__wasi_size_t * , nread)
|
||||
|
||||
m3ApiCheckMem(wasi_iovs, iovs_len * sizeof(wasi_iovec_t));
|
||||
m3ApiCheckMem(nread, sizeof(__wasi_size_t));
|
||||
|
||||
ssize_t res = 0;
|
||||
for (__wasi_size_t i = 0; i < iovs_len; i++) {
|
||||
void* addr = m3ApiOffsetToPtr(m3ApiReadMem32(&wasi_iovs[i].buf));
|
||||
size_t len = m3ApiReadMem32(&wasi_iovs[i].buf_len);
|
||||
if (len == 0) continue;
|
||||
|
||||
int ret = read (fd, addr, len);
|
||||
if (ret < 0) m3ApiReturn(errno_to_wasi(errno));
|
||||
res += ret;
|
||||
if ((size_t)ret < len) break;
|
||||
}
|
||||
m3ApiWriteMem32(nread, res);
|
||||
m3ApiReturn(__WASI_ERRNO_SUCCESS);
|
||||
}
|
||||
|
||||
m3ApiRawFunction(m3_wasi_generic_fd_write)
|
||||
{
|
||||
m3ApiReturnType (uint32_t)
|
||||
m3ApiGetArg (__wasi_fd_t , fd)
|
||||
m3ApiGetArgMem (wasi_iovec_t * , wasi_iovs)
|
||||
m3ApiGetArg (__wasi_size_t , iovs_len)
|
||||
m3ApiGetArgMem (__wasi_size_t * , nwritten)
|
||||
|
||||
m3ApiCheckMem(wasi_iovs, iovs_len * sizeof(wasi_iovec_t));
|
||||
m3ApiCheckMem(nwritten, sizeof(__wasi_size_t));
|
||||
|
||||
ssize_t res = 0;
|
||||
for (__wasi_size_t i = 0; i < iovs_len; i++) {
|
||||
void* addr = m3ApiOffsetToPtr(m3ApiReadMem32(&wasi_iovs[i].buf));
|
||||
size_t len = m3ApiReadMem32(&wasi_iovs[i].buf_len);
|
||||
if (len == 0) continue;
|
||||
|
||||
int ret = write (fd, addr, len);
|
||||
if (ret < 0) m3ApiReturn(errno_to_wasi(errno));
|
||||
res += ret;
|
||||
if ((size_t)ret < len) break;
|
||||
}
|
||||
m3ApiWriteMem32(nwritten, res);
|
||||
m3ApiReturn(__WASI_ERRNO_SUCCESS);
|
||||
}
|
||||
|
||||
m3ApiRawFunction(m3_wasi_generic_fd_close)
|
||||
{
|
||||
m3ApiReturnType (uint32_t)
|
||||
m3ApiGetArg (__wasi_fd_t, fd)
|
||||
|
||||
int ret = close(fd);
|
||||
m3ApiReturn(ret == 0 ? __WASI_ERRNO_SUCCESS : ret);
|
||||
}
|
||||
|
||||
m3ApiRawFunction(m3_wasi_generic_fd_datasync)
|
||||
{
|
||||
m3ApiReturnType (uint32_t)
|
||||
m3ApiGetArg (__wasi_fd_t, fd)
|
||||
|
||||
// TODO
|
||||
m3ApiReturn(__WASI_ERRNO_SUCCESS);
|
||||
}
|
||||
|
||||
m3ApiRawFunction(m3_wasi_generic_random_get)
|
||||
{
|
||||
m3ApiReturnType (uint32_t)
|
||||
m3ApiGetArgMem (uint8_t * , buf)
|
||||
m3ApiGetArg (__wasi_size_t , buf_len)
|
||||
|
||||
m3ApiCheckMem(buf, buf_len);
|
||||
|
||||
while (1) {
|
||||
ssize_t retlen = 0;
|
||||
|
||||
#if defined(__wasi__) || defined(__APPLE__) || defined(__ANDROID_API__) || defined(__OpenBSD__) || defined(__EMSCRIPTEN__)
|
||||
size_t reqlen = M3_MIN (buf_len, 256);
|
||||
# if defined(__APPLE__) && (TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR)
|
||||
retlen = SecRandomCopyBytes(kSecRandomDefault, reqlen, buf) < 0 ? -1 : reqlen;
|
||||
# else
|
||||
retlen = getentropy(buf, reqlen) < 0 ? -1 : reqlen;
|
||||
# endif
|
||||
#elif defined(__FreeBSD__) || defined(__linux__)
|
||||
retlen = getrandom(buf, buf_len, 0);
|
||||
#elif defined(_WIN32)
|
||||
if (RtlGenRandom(buf, buf_len) == TRUE) {
|
||||
m3ApiReturn(__WASI_ERRNO_SUCCESS);
|
||||
}
|
||||
#else
|
||||
m3ApiReturn(__WASI_ERRNO_NOSYS);
|
||||
#endif
|
||||
if (retlen < 0) {
|
||||
if (errno == EINTR || errno == EAGAIN) {
|
||||
continue;
|
||||
}
|
||||
m3ApiReturn(errno_to_wasi(errno));
|
||||
} else if (retlen == buf_len) {
|
||||
m3ApiReturn(__WASI_ERRNO_SUCCESS);
|
||||
} else {
|
||||
buf += retlen;
|
||||
buf_len -= retlen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m3ApiRawFunction(m3_wasi_generic_clock_res_get)
|
||||
{
|
||||
m3ApiReturnType (uint32_t)
|
||||
m3ApiGetArg (__wasi_clockid_t , wasi_clk_id)
|
||||
m3ApiGetArgMem (__wasi_timestamp_t * , resolution)
|
||||
|
||||
m3ApiCheckMem(resolution, sizeof(__wasi_timestamp_t));
|
||||
|
||||
int clk = convert_clockid(wasi_clk_id);
|
||||
if (clk < 0) m3ApiReturn(__WASI_ERRNO_INVAL);
|
||||
|
||||
struct timespec tp;
|
||||
if (clock_getres(clk, &tp) != 0) {
|
||||
m3ApiWriteMem64(resolution, 1000000);
|
||||
} else {
|
||||
m3ApiWriteMem64(resolution, convert_timespec(&tp));
|
||||
}
|
||||
|
||||
m3ApiReturn(__WASI_ERRNO_SUCCESS);
|
||||
}
|
||||
|
||||
m3ApiRawFunction(m3_wasi_generic_clock_time_get)
|
||||
{
|
||||
m3ApiReturnType (uint32_t)
|
||||
m3ApiGetArg (__wasi_clockid_t , wasi_clk_id)
|
||||
m3ApiGetArg (__wasi_timestamp_t , precision)
|
||||
m3ApiGetArgMem (__wasi_timestamp_t * , time)
|
||||
|
||||
m3ApiCheckMem(time, sizeof(__wasi_timestamp_t));
|
||||
|
||||
int clk = convert_clockid(wasi_clk_id);
|
||||
if (clk < 0) m3ApiReturn(__WASI_ERRNO_INVAL);
|
||||
|
||||
struct timespec tp;
|
||||
if (clock_gettime(clk, &tp) != 0) {
|
||||
m3ApiReturn(errno_to_wasi(errno));
|
||||
}
|
||||
|
||||
m3ApiWriteMem64(time, convert_timespec(&tp));
|
||||
m3ApiReturn(__WASI_ERRNO_SUCCESS);
|
||||
}
|
||||
|
||||
m3ApiRawFunction(m3_wasi_generic_proc_exit)
|
||||
{
|
||||
m3ApiGetArg (uint32_t, code)
|
||||
|
||||
m3_wasi_context_t* context = (m3_wasi_context_t*)(_ctx->userdata);
|
||||
|
||||
if (context) {
|
||||
context->exit_code = code;
|
||||
}
|
||||
|
||||
m3ApiTrap(m3Err_trapExit);
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
M3Result SuppressLookupFailure(M3Result i_result)
|
||||
{
|
||||
if (i_result == m3Err_functionLookupFailed)
|
||||
return m3Err_none;
|
||||
else
|
||||
return i_result;
|
||||
}
|
||||
|
||||
m3_wasi_context_t* m3_GetWasiContext()
|
||||
{
|
||||
return wasi_context;
|
||||
}
|
||||
|
||||
|
||||
M3Result m3_LinkEspWASI (IM3Module module)
|
||||
{
|
||||
M3Result result = m3Err_none;
|
||||
|
||||
// TODO: Preopen dirs
|
||||
|
||||
if (!wasi_context) {
|
||||
wasi_context = (m3_wasi_context_t*)malloc(sizeof(m3_wasi_context_t));
|
||||
wasi_context->exit_code = 0;
|
||||
wasi_context->argc = 0;
|
||||
wasi_context->argv = 0;
|
||||
}
|
||||
|
||||
static const char* namespaces[2] = { "wasi_unstable", "wasi_snapshot_preview1" };
|
||||
|
||||
// fd_seek is incompatible
|
||||
_ (SuppressLookupFailure (m3_LinkRawFunction (module, "wasi_unstable", "fd_seek", "i(iIi*)", &m3_wasi_unstable_fd_seek)));
|
||||
_ (SuppressLookupFailure (m3_LinkRawFunction (module, "wasi_snapshot_preview1", "fd_seek", "i(iIi*)", &m3_wasi_snapshot_preview1_fd_seek)));
|
||||
|
||||
for (int i=0; i<2; i++)
|
||||
{
|
||||
const char* wasi = namespaces[i];
|
||||
|
||||
_ (SuppressLookupFailure (m3_LinkRawFunctionEx (module, wasi, "args_get", "i(**)", &m3_wasi_generic_args_get, wasi_context)));
|
||||
_ (SuppressLookupFailure (m3_LinkRawFunctionEx (module, wasi, "args_sizes_get", "i(**)", &m3_wasi_generic_args_sizes_get, wasi_context)));
|
||||
_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "clock_res_get", "i(i*)", &m3_wasi_generic_clock_res_get)));
|
||||
_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "clock_time_get", "i(iI*)", &m3_wasi_generic_clock_time_get)));
|
||||
_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "environ_get", "i(**)", &m3_wasi_generic_environ_get)));
|
||||
_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "environ_sizes_get", "i(**)", &m3_wasi_generic_environ_sizes_get)));
|
||||
|
||||
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_advise", "i(iIIi)", )));
|
||||
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_allocate", "i(iII)", )));
|
||||
_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_close", "i(i)", &m3_wasi_generic_fd_close)));
|
||||
_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_datasync", "i(i)", &m3_wasi_generic_fd_datasync)));
|
||||
_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_fdstat_get", "i(i*)", &m3_wasi_generic_fd_fdstat_get)));
|
||||
_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_fdstat_set_flags", "i(ii)", &m3_wasi_generic_fd_fdstat_set_flags)));
|
||||
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_fdstat_set_rights", "i(iII)", )));
|
||||
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_filestat_get", "i(i*)", )));
|
||||
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_filestat_set_size", "i(iI)", )));
|
||||
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_filestat_set_times","i(iIIi)", )));
|
||||
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_pread", "i(i*iI*)",)));
|
||||
_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_prestat_get", "i(i*)", &m3_wasi_generic_fd_prestat_get)));
|
||||
_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_prestat_dir_name", "i(i*i)", &m3_wasi_generic_fd_prestat_dir_name)));
|
||||
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_pwrite", "i(i*iI*)",)));
|
||||
_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_read", "i(i*i*)", &m3_wasi_generic_fd_read)));
|
||||
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_readdir", "i(i*iI*)",)));
|
||||
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_renumber", "i(ii)", )));
|
||||
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_sync", "i(i)", )));
|
||||
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_tell", "i(i*)", )));
|
||||
_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_write", "i(i*i*)", &m3_wasi_generic_fd_write)));
|
||||
|
||||
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_create_directory", "i(i*i)", )));
|
||||
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_filestat_get", "i(ii*i*)", &m3_wasi_generic_path_filestat_get)));
|
||||
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_filestat_set_times", "i(ii*iIIi)", )));
|
||||
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_link", "i(ii*ii*i)", )));
|
||||
_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_open", "i(ii*iiIIi*)", &m3_wasi_generic_path_open)));
|
||||
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_readlink", "i(i*i*i*)", )));
|
||||
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_remove_directory", "i(i*i)", )));
|
||||
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_rename", "i(i*ii*i)", )));
|
||||
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_symlink", "i(*ii*i)", )));
|
||||
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_unlink_file", "i(i*i)", )));
|
||||
|
||||
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "poll_oneoff", "i(**i*)", &m3_wasi_generic_poll_oneoff)));
|
||||
_ (SuppressLookupFailure (m3_LinkRawFunctionEx (module, wasi, "proc_exit", "v(i)", &m3_wasi_generic_proc_exit, wasi_context)));
|
||||
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "proc_raise", "i(i)", )));
|
||||
_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "random_get", "i(*i)", &m3_wasi_generic_random_get)));
|
||||
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "sched_yield", "i()", )));
|
||||
|
||||
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "sock_recv", "i(i*ii**)", )));
|
||||
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "sock_send", "i(i*ii*)", )));
|
||||
//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "sock_shutdown", "i(ii)", )));
|
||||
}
|
||||
|
||||
_catch:
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // ESP32
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
//
|
||||
// m3_api_esp_wasi.h
|
||||
//
|
||||
// Created by Volodymyr Shymanskyy on 01/07/20.
|
||||
// Copyright © 2019 Volodymyr Shymanskyy. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef m3_api_esp_wasi_h
|
||||
#define m3_api_esp_wasi_h
|
||||
|
||||
#include "m3_core.h"
|
||||
|
||||
d_m3BeginExternC
|
||||
|
||||
typedef struct m3_wasi_context_t
|
||||
{
|
||||
i32 exit_code;
|
||||
u32 argc;
|
||||
ccstr_t * argv;
|
||||
} m3_wasi_context_t;
|
||||
|
||||
M3Result m3_LinkEspWASI (IM3Module io_module);
|
||||
|
||||
m3_wasi_context_t* m3_GetWasiContext();
|
||||
|
||||
d_m3EndExternC
|
||||
|
||||
#endif /* m3_api_esp_wasi_h */
|
|
@ -0,0 +1,76 @@
|
|||
//
|
||||
// Wasm3 - high performance WebAssembly interpreter written in C.
|
||||
//
|
||||
// Copyright © 2019 Steven Massey, Volodymyr Shymanskyy.
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
#include "esp_system.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "wasm3.h"
|
||||
#include "m3_env.h"
|
||||
|
||||
#include "m3_api_esp_wasi.h"
|
||||
#include "wasi_test.wasm.h"
|
||||
|
||||
#define FATAL(msg, ...) { printf("Fatal: " msg "\n", ##__VA_ARGS__); return; }
|
||||
|
||||
static void run_wasm(void)
|
||||
{
|
||||
M3Result result = m3Err_none;
|
||||
|
||||
uint8_t* wasm = (uint8_t*)wasi_test_wasm;
|
||||
uint32_t fsize = wasi_test_wasm_len;
|
||||
|
||||
printf("Loading WebAssembly...\n");
|
||||
IM3Environment env = m3_NewEnvironment ();
|
||||
if (!env) FATAL("m3_NewEnvironment failed");
|
||||
|
||||
IM3Runtime runtime = m3_NewRuntime (env, 8*1024, NULL);
|
||||
if (!runtime) FATAL("m3_NewRuntime failed");
|
||||
|
||||
IM3Module module;
|
||||
result = m3_ParseModule (env, &module, wasm, fsize);
|
||||
if (result) FATAL("m3_ParseModule: %s", result);
|
||||
|
||||
result = m3_LoadModule (runtime, module);
|
||||
if (result) FATAL("m3_LoadModule: %s", result);
|
||||
|
||||
result = m3_LinkEspWASI (runtime->modules);
|
||||
if (result) FATAL("m3_LinkEspWASI: %s", result);
|
||||
|
||||
IM3Function f;
|
||||
result = m3_FindFunction (&f, runtime, "_start");
|
||||
if (result) FATAL("m3_FindFunction: %s", result);
|
||||
|
||||
printf("Running...\n");
|
||||
|
||||
const char* i_argv[2] = { "test.wasm", NULL };
|
||||
|
||||
m3_wasi_context_t* wasi_ctx = m3_GetWasiContext();
|
||||
wasi_ctx->argc = 1;
|
||||
wasi_ctx->argv = i_argv;
|
||||
|
||||
result = m3_CallV (f);
|
||||
|
||||
if (result) FATAL("m3_Call: %s", result);
|
||||
}
|
||||
|
||||
extern "C" void app_main(void)
|
||||
{
|
||||
printf("\nWasm3 v" M3_VERSION " on " CONFIG_IDF_TARGET ", build " __DATE__ " " __TIME__ "\n");
|
||||
|
||||
clock_t start = clock();
|
||||
run_wasm();
|
||||
clock_t end = clock();
|
||||
|
||||
printf("Elapsed: %ld ms\n", (end - start)*1000 / CLOCKS_PER_SEC);
|
||||
|
||||
sleep(3);
|
||||
printf("Restarting...\n\n\n");
|
||||
esp_restart();
|
||||
}
|