diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 5ddba8f3e..7ccfa2467 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -12,7 +12,7 @@ ENV TZ=Asian/Shanghai # hadolint ignore=DL3008 RUN apt-get update \ && apt-get install -y apt-transport-https apt-utils build-essential \ - ca-certificates curl g++-multilib git gnupg \ + ca-certificates ccache curl g++-multilib git gnupg \ libgcc-9-dev lib32gcc-9-dev lsb-release \ ninja-build ocaml ocamlbuild python2.7 \ software-properties-common tree tzdata \ @@ -20,6 +20,15 @@ RUN apt-get update \ && apt-get clean -y \ && rm -rf /var/lib/apt/lists/* +# +# binaryen +ARG BINARYEN_VER=111 +WORKDIR /opt +RUN wget -c --progress=dot:giga https://github.com/WebAssembly/binaryen/releases/download/version_${BINARYEN_VER}/binaryen-version_${BINARYEN_VER}-x86_64-linux.tar.gz \ + && tar xf binaryen-version_${BINARYEN_VER}-x86_64-linux.tar.gz \ + && ln -sf /opt/binaryen-version_111 /opt/binaryen \ + && rm binaryen-version_${BINARYEN_VER}-x86_64-linux.tar.gz + # # CMAKE (https://apt.kitware.com/) SHELL ["/bin/bash", "-o", "pipefail", "-c"] @@ -31,25 +40,26 @@ RUN wget --progress=dot:giga -O - https://apt.kitware.com/keys/kitware-archive-l && apt-get install -y kitware-archive-keyring --no-install-recommends \ && apt-get install -y cmake --no-install-recommends \ && apt-get clean -y \ - && rm -rf /var/lib/apt/lists/* + && rm -rf /var/lib/apt/lists/* # # install emsdk WORKDIR /opt RUN git clone https://github.com/emscripten-core/emsdk.git +ARG EMSDK_VER=3.0.0 WORKDIR /opt/emsdk RUN git pull \ - && ./emsdk install 2.0.26 \ - && ./emsdk activate 2.0.26 \ + && ./emsdk install ${EMSDK_VER} \ + && ./emsdk activate ${EMSDK_VER} \ && echo "source /opt/emsdk/emsdk_env.sh" >> /root/.bashrc # # install wasi-sdk -ARG WASI_SDK_VER=16 +ARG WASI_SDK_VER=19 RUN wget -c --progress=dot:giga https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_SDK_VER}/wasi-sdk-${WASI_SDK_VER}.0-linux.tar.gz -P /opt \ && tar xf /opt/wasi-sdk-${WASI_SDK_VER}.0-linux.tar.gz -C /opt \ - && ln -fs /opt/wasi-sdk-${WASI_SDK_VER}.0 /opt/wasi-sdk \ + && ln -sf /opt/wasi-sdk-${WASI_SDK_VER}.0 /opt/wasi-sdk \ && rm /opt/wasi-sdk-${WASI_SDK_VER}.0-linux.tar.gz # @@ -57,29 +67,29 @@ RUN wget -c --progress=dot:giga https://github.com/WebAssembly/wasi-sdk/releases ARG WABT_VER=1.0.29 RUN wget -c --progress=dot:giga https://github.com/WebAssembly/wabt/releases/download/${WABT_VER}/wabt-${WABT_VER}-ubuntu.tar.gz -P /opt \ && tar xf /opt/wabt-${WABT_VER}-ubuntu.tar.gz -C /opt \ - && ln -fs /opt/wabt-${WABT_VER} /opt/wabt \ + && ln -sf /opt/wabt-${WABT_VER} /opt/wabt \ && rm /opt/wabt-${WABT_VER}-ubuntu.tar.gz # # install bazelisk ARG BAZELISK_VER=1.12.0 -RUN mkdir /opt/bazelisk \ +RUN mkdir /opt/bazelisk \ && wget -c --progress=dot:giga https://github.com/bazelbuild/bazelisk/releases/download/v${BAZELISK_VER}/bazelisk-linux-amd64 -P /opt/bazelisk \ && chmod a+x /opt/bazelisk/bazelisk-linux-amd64 \ && ln -fs /opt/bazelisk/bazelisk-linux-amd64 /opt/bazelisk/bazel # # install clang+llvm +ARG LLVM_VER=14 +RUN apt-get purge -y clang-10 llvm-10 && apt autoremove -y WORKDIR /etc/apt/apt.conf.d RUN touch 99verfiy-peer.conf \ && echo "Acquire { https::Verify-Peer false }" > 99verfiy-peer.conf -WORKDIR /tmp +WORKDIR /tmp RUN wget --progress=dot:giga https://apt.llvm.org/llvm.sh \ && chmod a+x ./llvm.sh \ - && /tmp/llvm.sh 12 all \ - && ln -sf /usr/bin/clang-format-12 /usr/bin/clang-format \ - && rm -rf /tmp/* + && ./llvm.sh ${LLVM_VER} all # # [Optional] @@ -96,17 +106,28 @@ RUN apt-get update \ # Install required python packages # hadolint ignore=DL3013 RUN python3 -m pip install --no-cache-dir --upgrade pip \ - && pip3 install --no-cache-dir --user black nose pycparser pylint + && pip3 install --no-cache-dir black nose pycparser pylint -# set path, PS and clean up -ENV PATH "/opt/bazelisk:/opt/clang-llvm/bin:${PATH}" -RUN echo "export PATH=/opt/bazelisk:/opt/clang-llvm/bin:${PATH}" >> /root/.bashrc \ - && printf "%s\n" "PS1='\n[ \u@wamr-dev-docker \W ]\n$ '" >> /root/.bashrc \ +# +# Install github-cli. It doens't work as a feature of devcontainer.json +RUN cd /tmp \ + && wget https://github.com/cli/cli/releases/download/v2.20.2/gh_2.20.2_linux_amd64.deb \ + && dpkg -i gh_2.20.2_linux_amd64.deb + +# +# Install NodeJS +RUN curl -fsSL https://deb.nodesource.com/setup_19.x | bash - +RUN apt-get install -y nodejs + +# set path +ENV PATH="/opt/bazelisk:/usr/lib/llvm-${LLVM_VER}/bin:${PATH}" +ENV CC=/usr/lib/llvm-${LLVM_VER}/bin/clang CXX=/usr/lib/llvm-${LLVM_VER}/bin/clang++ +RUN printf "%s\n" "PS1='\n[ \u@wamr-dev-docker \W ]\n$ '" >> /root/.bashrc \ && apt-get autoremove -y \ && apt-get clean -y \ && rm -rf /var/lib/apt/lists/* \ && rm -rf /tmp/* # set workdir when container run -VOLUME /workspace -WORKDIR /workspace \ No newline at end of file +VOLUME /workspaces +WORKDIR /workspaces diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 976f5beb7..24e1bdfd6 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,6 +1,5 @@ // Copyright (C) 2019 Intel Corporation. All rights reserved. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - // For format details, see https://aka.ms/vscode-remote/devcontainer.json or this file's README at: // https://github.com/microsoft/vscode-dev-containers/tree/v0.195.0/containers/cpp { @@ -10,7 +9,12 @@ // Update 'VARIANT' to pick an Debian / Ubuntu OS version: debian-11, debian-10, debian-9, ubuntu-21.04, ubuntu-20.04, ubuntu-18.04 // Use Debian 11, Debian 9, Ubuntu 18.04 or Ubuntu 21.04 on local arm64/Apple Silicon "args": { - "VARIANT": "ubuntu-20.04" + "BINARYEN_VER": "111", + "EMSDK_VER": "3.0.0", + "LLVM_VER": "15", + "VARIANT": "ubuntu-20.04", + "WASI_SDK_VER": "19", + "WABT_VER": "1.0.31" } }, "runArgs": [ @@ -27,12 +31,10 @@ // Add the IDs of extensions you want installed when the container is created. "extensions": [ "dtsvet.vscode-wasm", - "esbenp.prettier-vscode", + "llvm-vs-code-extensions.vscode-clangd", "ms-python.python", "ms-python.vscode-pylance", "ms-vscode.cmake-tools", - "ms-vscode.cpptools", - "twxs.cmake" ] } }, diff --git a/.github/workflows/build_iwasm_release.yml b/.github/workflows/build_iwasm_release.yml index 9a47c6c4d..557a81fc4 100644 --- a/.github/workflows/build_iwasm_release.yml +++ b/.github/workflows/build_iwasm_release.yml @@ -36,7 +36,7 @@ jobs: - name: generate iwasm binary release run: | cmake -S . -B build \ - -DWAMR_BUILD_AOT=1 -DWAMR_BUILD_INTERP=1 -DWAMR_BUILD_JIT=0 \ + -DWAMR_BUILD_AOT=1 -DWAMR_BUILD_INTERP=1 -DWAMR_BUILD_FAST_JIT=1 -DWAMR_BUILD_JIT=1 \ -DWAMR_BUILD_CUSTOM_NAME_SECTION=0 \ -DWAMR_BUILD_DEBUG_INTERP=0 \ -DWAMR_BUILD_DEBUG_AOT=0 \ diff --git a/.github/workflows/build_llvm_libraries.yml b/.github/workflows/build_llvm_libraries.yml index 6eaa2be7c..7ef1dd63f 100644 --- a/.github/workflows/build_llvm_libraries.yml +++ b/.github/workflows/build_llvm_libraries.yml @@ -5,23 +5,46 @@ name: Reusable workflow-build_llvm_libraries on: workflow_call: inputs: - runs-on: + os: required: true type: string + arch: + required: true + type: string + outputs: + cache_key: + description: "A cached key of LLVM libraries" + value: ${{ jobs.build_llvm_libraries.outputs.key}} jobs: build_llvm_libraries: - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: ${{ fromJson(inputs.runs-on) }} + runs-on: ${{ inputs.os }} + outputs: + key: ${{ steps.create_lib_cache_key.outputs.key}} steps: - name: checkout uses: actions/checkout@v3 + - name: install dependencies + run: /usr/bin/env python3 -m pip install -r requirements.txt + working-directory: build-scripts + + - name: retrive the last commit ID + id: get_last_commit + run: echo "last_commit=$(GH_TOKEN=${{ secrets.GITHUB_TOKEN }} /usr/bin/env python3 ./build_llvm.py --llvm-ver)" >> $GITHUB_OUTPUT + working-directory: build-scripts + + # Bump the prefix number to evict all previous caches and + # enforce a clean build, in the unlikely case that some + # weird build error occur and llvm/build becomes a potential + # suspect. + - name: form the cache key of libraries + id: create_lib_cache_key + run: echo "key=0-llvm-libraries-${{ inputs.os }}-${{ inputs.arch }}-${{ steps.get_last_commit.outputs.last_commit }}" >> $GITHUB_OUTPUT + - name: Cache LLVM libraries - id: cache_llvm + id: retrieve_llvm_libs uses: actions/cache@v3 with: path: | @@ -30,10 +53,39 @@ jobs: ./core/deps/llvm/build/lib ./core/deps/llvm/build/libexec ./core/deps/llvm/build/share - key: ${{ matrix.os }}-build-llvm_libraries_ex + key: ${{ steps.create_lib_cache_key.outputs.key}} - - name: Build llvm - id: build_llvm - if: ${{ steps.cache_llvm.outputs.cache-hit != 'true' }} - run: /usr/bin/env python3 ./build_llvm.py --arch X86 WebAssembly + - uses: actions/cache@v3 + with: + path: ~/.ccache + key: 0-ccache-${{ inputs.os }}-${{ steps.get_last_commit.outputs.last_commit }} + restore-keys: | + 0-ccache-${{ inputs.os }} + if: steps.retrieve_llvm_libs.outputs.cache-hit != 'true' && inputs.os == 'ubuntu-20.04' + + - uses: actions/cache@v3 + with: + path: ~/.cache/ccache + key: 0-ccache-${{ inputs.os }}-${{ steps.get_last_commit.outputs.last_commit }} + restore-keys: | + 0-ccache-${{ inputs.os }} + if: steps.retrieve_llvm_libs.outputs.cache-hit != 'true' && inputs.os == 'ubuntu-22.04' + + - run: sudo apt install -y ccache ninja-build + if: steps.retrieve_llvm_libs.outputs.cache-hit != 'true' && startsWith(inputs.os, 'ubuntu') + + - uses: actions/cache@v3 + with: + path: ~/Library/Caches/ccache + key: 0-ccache-${{ inputs.os }}-${{ steps.get_last_commit.outputs.last_commit }} + restore-keys: | + 0-ccache-${{ inputs.os }} + if: steps.retrieve_llvm_libs.outputs.cache-hit != 'true' && startsWith(inputs.os, 'macos') + + - run: brew install ccache ninja + if: steps.retrieve_llvm_libs.outputs.cache-hit != 'true' && startsWith(inputs.os, 'macos') + + - name: Build LLVM libraries + if: steps.retrieve_llvm_libs.outputs.cache-hit != 'true' + run: /usr/bin/env python3 ./build_llvm.py --arch ${{ inputs.arch }} working-directory: build-scripts diff --git a/.github/workflows/build_wamr_vscode_ext.yml b/.github/workflows/build_wamr_vscode_ext.yml index 81c938e6f..f62a4bc98 100644 --- a/.github/workflows/build_wamr_vscode_ext.yml +++ b/.github/workflows/build_wamr_vscode_ext.yml @@ -32,11 +32,14 @@ jobs: working-directory: test-tools/wamr-ide/VSCode-Extension - name: generate wamr ide vscode extension + env: + credentials: ${{ secrets.TOKEN }} run: | npm install -g vsce rm -rf node_modules npm install vsce package + vsce publish -p ${{ secrets.TOKEN }} working-directory: test-tools/wamr-ide/VSCode-Extension - name: compress the vscode extension diff --git a/.github/workflows/codeing_guildelines.yml b/.github/workflows/coding_guidelines.yml similarity index 97% rename from .github/workflows/codeing_guildelines.yml rename to .github/workflows/coding_guidelines.yml index bc66436c8..259e84fe5 100644 --- a/.github/workflows/codeing_guildelines.yml +++ b/.github/workflows/coding_guidelines.yml @@ -15,7 +15,7 @@ concurrency: cancel-in-progress: true jobs: - complinace_job: + compliance_job: runs-on: ubuntu-latest steps: - name: checkout diff --git a/.github/workflows/compilation_on_android_ubuntu.yml b/.github/workflows/compilation_on_android_ubuntu.yml index 06a921001..0fa9acb09 100644 --- a/.github/workflows/compilation_on_android_ubuntu.yml +++ b/.github/workflows/compilation_on_android_ubuntu.yml @@ -53,33 +53,47 @@ env: FAST_JIT_BUILD_OPTIONS: " -DWAMR_BUILD_AOT=1 -DWAMR_BUILD_FAST_INTERP=0 -DWAMR_BUILD_INTERP=0 -DWAMR_BUILD_FAST_JIT=1 -DWAMR_BUILD_JIT=0 -DWAMR_BUILD_LAZY_JIT=0" LLVM_LAZY_JIT_BUILD_OPTIONS: " -DWAMR_BUILD_AOT=1 -DWAMR_BUILD_FAST_INTERP=0 -DWAMR_BUILD_INTERP=0 -DWAMR_BUILD_FAST_JIT=0 -DWAMR_BUILD_JIT=1 -DWAMR_BUILD_LAZY_JIT=1" LLVM_EAGER_JIT_BUILD_OPTIONS: "-DWAMR_BUILD_AOT=1 -DWAMR_BUILD_FAST_INTERP=0 -DWAMR_BUILD_INTERP=0 -DWAMR_BUILD_FAST_JIT=0 -DWAMR_BUILD_JIT=1 -DWAMR_BUILD_LAZY_JIT=0" - # LLVM - LLVM_CACHE_SUFFIX: "build-llvm_libraries_ex" + MULTI_TIER_JIT_BUILD_OPTIONS: "-DWAMR_BUILD_AOT=1 -DWAMR_BUILD_FAST_INTERP=0 -DWAMR_BUILD_INTERP=1 -DWAMR_BUILD_FAST_JIT=1 -DWAMR_BUILD_JIT=1 -DWAMR_BUILD_LAZY_JIT=1" # For Spec Test DEFAULT_TEST_OPTIONS: "-s spec -b -P" MULTI_MODULES_TEST_OPTIONS: "-s spec -b -M -P" SIMD_TEST_OPTIONS: "-s spec -b -S -P" THREADS_TEST_OPTIONS: "-s spec -b -p -P" X86_32_TARGET_TEST_OPTIONS: "-m x86_32 -P" + WASI_TEST_OPTIONS: "-s wasi_certification -w" jobs: - build_llvm_libraries: + build_llvm_libraries_on_ubuntu_2004: uses: ./.github/workflows/build_llvm_libraries.yml with: - runs-on: "['ubuntu-20.04', 'ubuntu-22.04']" + os: "ubuntu-20.04" + arch: "X86" + + build_llvm_libraries_on_ubuntu_2204: + uses: ./.github/workflows/build_llvm_libraries.yml + with: + os: "ubuntu-22.04" + arch: "X86" build_wamrc: - needs: [build_llvm_libraries] + needs: + [build_llvm_libraries_on_ubuntu_2004, build_llvm_libraries_on_ubuntu_2204] runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-20.04, ubuntu-22.04] + include: + - os: ubuntu-20.04 + llvm_cache_key: ${{ needs.build_llvm_libraries_on_ubuntu_2004.outputs.cache_key }} + - os: ubuntu-22.04 + llvm_cache_key: ${{ needs.build_llvm_libraries_on_ubuntu_2204.outputs.cache_key }} steps: - name: checkout uses: actions/checkout@v3 + # since jobs.id can't contain the dot character + # it is hard to use `format` to assemble the cache key - name: Get LLVM libraries - id: cache_llvm + id: retrieve_llvm_libs uses: actions/cache@v3 with: path: | @@ -88,10 +102,10 @@ jobs: ./core/deps/llvm/build/lib ./core/deps/llvm/build/libexec ./core/deps/llvm/build/share - key: ${{ matrix.os }}-${{ env.LLVM_CACHE_SUFFIX }} + key: ${{ matrix.llvm_cache_key }} - name: Quit if cache miss - if: steps.cache_llvm.outputs.cache-hit != 'true' + if: steps.retrieve_llvm_libs.outputs.cache-hit != 'true' run: echo "::error::can not get prebuilt llvm libraries" && exit 1 - name: Build wamrc @@ -102,7 +116,8 @@ jobs: working-directory: wamr-compiler build_iwasm: - needs: [build_llvm_libraries] + needs: + [build_llvm_libraries_on_ubuntu_2004, build_llvm_libraries_on_ubuntu_2204] runs-on: ${{ matrix.os }} strategy: matrix: @@ -114,6 +129,7 @@ jobs: $FAST_JIT_BUILD_OPTIONS, $LLVM_LAZY_JIT_BUILD_OPTIONS, $LLVM_EAGER_JIT_BUILD_OPTIONS, + $MULTI_TIER_JIT_BUILD_OPTIONS, ] make_options_feature: [ # Features @@ -146,6 +162,8 @@ jobs: make_options_feature: "-DWAMR_BUILD_MULTI_MODULE=1" - make_options_run_mode: $LLVM_EAGER_JIT_BUILD_OPTIONS make_options_feature: "-DWAMR_BUILD_MULTI_MODULE=1" + - make_options_run_mode: $MULTI_TIER_JIT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_MULTI_MODULE=1" # SIMD only on JIT/AOT mode - make_options_run_mode: $CLASSIC_INTERP_BUILD_OPTIONS make_options_feature: "-DWAMR_BUILD_SIMD=1" @@ -162,6 +180,8 @@ jobs: make_options_feature: "-DWAMR_BUILD_DEBUG_INTERP=1" - make_options_run_mode: $LLVM_EAGER_JIT_BUILD_OPTIONS make_options_feature: "-DWAMR_BUILD_DEBUG_INTERP=1" + - make_options_run_mode: $MULTI_TIER_JIT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_DEBUG_INTERP=1" # DEBUG_AOT only on JIT/AOT mode - make_options_run_mode: $CLASSIC_INTERP_BUILD_OPTIONS make_options_feature: "-DWAMR_BUILD_DEBUG_AOT=1" @@ -174,6 +194,8 @@ jobs: make_options_feature: "-DWAMR_BUILD_DEBUG_AOT=1" - make_options_run_mode: $LLVM_EAGER_JIT_BUILD_OPTIONS make_options_feature: "-DWAMR_BUILD_DEBUG_AOT=1" + - make_options_run_mode: $MULTI_TIER_JIT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_DEBUG_AOT=1" # MINI_LOADER only on INTERP mode - make_options_run_mode: $AOT_BUILD_OPTIONS make_options_feature: "-DWAMR_BUILD_MINI_LOADER=1" @@ -183,16 +205,28 @@ jobs: make_options_feature: "-DWAMR_BUILD_MINI_LOADER=1" - make_options_run_mode: $LLVM_EAGER_JIT_BUILD_OPTIONS make_options_feature: "-DWAMR_BUILD_MINI_LOADER=1" - # Fast-JIT mode doesn't support android(X86-32) + - make_options_run_mode: $MULTI_TIER_JIT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_MINI_LOADER=1" + # Fast-JIT and Multi-Tier-JIT mode don't support android(X86-32) - make_options_run_mode: $FAST_JIT_BUILD_OPTIONS platform: android + - make_options_run_mode: $MULTI_TIER_JIT_BUILD_OPTIONS + platform: android + # only test andorid on ubuntu latest + - os: ubuntu-20.04 + platform: android + include: + - os: ubuntu-20.04 + llvm_cache_key: ${{ needs.build_llvm_libraries_on_ubuntu_2004.outputs.cache_key }} + - os: ubuntu-22.04 + llvm_cache_key: ${{ needs.build_llvm_libraries_on_ubuntu_2204.outputs.cache_key }} steps: - name: checkout uses: actions/checkout@v3 # only download llvm cache when needed - name: Get LLVM libraries - id: cache_llvm + id: retrieve_llvm_libs if: endsWith(matrix.make_options_run_mode, '_JIT_BUILD_OPTIONS') uses: actions/cache@v3 with: @@ -202,10 +236,10 @@ jobs: ./core/deps/llvm/build/lib ./core/deps/llvm/build/libexec ./core/deps/llvm/build/share - key: ${{ matrix.os }}-${{ env.LLVM_CACHE_SUFFIX }} + key: ${{ matrix.llvm_cache_key }} - name: Quit if cache miss - if: endsWith(matrix.make_options_run_mode, '_JIT_BUILD_OPTIONS') && (steps.cache_llvm.outputs.cache-hit != 'true') + if: endsWith(matrix.make_options_run_mode, '_JIT_BUILD_OPTIONS') && (steps.retrieve_llvm_libs.outputs.cache-hit != 'true') run: echo "::error::can not get prebuilt llvm libraries" && exit 1 - name: Build iwasm @@ -216,7 +250,13 @@ jobs: working-directory: product-mini/platforms/${{ matrix.platform }} build_samples_wasm_c_api: - needs: [build_iwasm, build_llvm_libraries, build_wamrc] + needs: + [ + build_iwasm, + build_llvm_libraries_on_ubuntu_2004, + build_llvm_libraries_on_ubuntu_2204, + build_wamrc, + ] runs-on: ${{ matrix.os }} strategy: matrix: @@ -228,22 +268,28 @@ jobs: $FAST_JIT_BUILD_OPTIONS, $LLVM_LAZY_JIT_BUILD_OPTIONS, $LLVM_EAGER_JIT_BUILD_OPTIONS, + $MULTI_TIER_JIT_BUILD_OPTIONS, ] os: [ubuntu-20.04, ubuntu-22.04] wasi_sdk_release: [ - "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-12/wasi-sdk-12.0-linux.tar.gz", + "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-19/wasi-sdk-19.0-linux.tar.gz", ] wabt_release: [ - "https://github.com/WebAssembly/wabt/releases/download/1.0.24/wabt-1.0.24-ubuntu.tar.gz", + "https://github.com/WebAssembly/wabt/releases/download/1.0.31/wabt-1.0.31-ubuntu.tar.gz", ] + include: + - os: ubuntu-20.04 + llvm_cache_key: ${{ needs.build_llvm_libraries_on_ubuntu_2004.outputs.cache_key }} + - os: ubuntu-22.04 + llvm_cache_key: ${{ needs.build_llvm_libraries_on_ubuntu_2204.outputs.cache_key }} steps: - name: checkout uses: actions/checkout@v3 - name: Get LLVM libraries - id: cache_llvm + id: retrieve_llvm_libs if: (!endsWith(matrix.make_options, '_INTERP_BUILD_OPTIONS')) uses: actions/cache@v3 with: @@ -253,18 +299,18 @@ jobs: ./core/deps/llvm/build/lib ./core/deps/llvm/build/libexec ./core/deps/llvm/build/share - key: ${{ matrix.os }}-${{ env.LLVM_CACHE_SUFFIX }} + key: ${{ matrix.llvm_cache_key }} - name: Quit if cache miss - if: (!endsWith(matrix.make_options, '_INTERP_BUILD_OPTIONS')) && (steps.cache_llvm.outputs.cache-hit != 'true') + if: (!endsWith(matrix.make_options, '_INTERP_BUILD_OPTIONS')) && (steps.retrieve_llvm_libs.outputs.cache-hit != 'true') run: echo "::error::can not get prebuilt llvm libraries" && exit 1 - name: download and install wabt run: | cd /opt sudo wget ${{ matrix.wabt_release }} - sudo tar -xzf wabt-1.0.24-*.tar.gz - sudo mv wabt-1.0.24 wabt + sudo tar -xzf wabt-1.0.31-*.tar.gz + sudo mv wabt-1.0.31 wabt - name: Build wamrc if: (!endsWith(matrix.make_options, '_INTERP_BUILD_OPTIONS')) @@ -276,19 +322,9 @@ jobs: - name: Build Sample [wasm-c-api] run: | - mkdir build && cd build - cmake .. ${{ matrix.make_options }} - cmake --build . --config Release --parallel 4 - ./callback - ./callback_chain - ./empty_imports - ./global - ./hello - ./hostref - ./memory - ./reflect - ./table - ./trap + cmake -S . -B build ${{ matrix.make_options }} + cmake --build build --config Release --parallel 4 + ctest --test-dir build working-directory: samples/wasm-c-api build_samples_others: @@ -297,14 +333,13 @@ jobs: strategy: matrix: os: [ubuntu-20.04, ubuntu-22.04] - wasi_sdk_release: - [ - "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-12/wasi-sdk-12.0-linux.tar.gz", - ] - wabt_release: - [ - "https://github.com/WebAssembly/wabt/releases/download/1.0.24/wabt-1.0.24-ubuntu.tar.gz", - ] + include: + - os: ubuntu-20.04 + wasi_sdk_release: "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-19/wasi-sdk-19.0-linux.tar.gz" + wabt_release: "https://github.com/WebAssembly/wabt/releases/download/1.0.31/wabt-1.0.31-ubuntu.tar.gz" + - os: ubuntu-22.04 + wasi_sdk_release: "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-19/wasi-sdk-19.0-linux.tar.gz" + wabt_release: "https://github.com/WebAssembly/wabt/releases/download/1.0.31/wabt-1.0.31-ubuntu.tar.gz" steps: - name: checkout uses: actions/checkout@v3 @@ -313,15 +348,31 @@ jobs: run: | cd /opt sudo wget ${{ matrix.wasi_sdk_release }} - sudo tar -xzf wasi-sdk-12.0-*.tar.gz - sudo mv wasi-sdk-12.0 wasi-sdk + sudo tar -xzf wasi-sdk-*.tar.gz + sudo mv wasi-sdk-19.0 wasi-sdk - name: download and install wabt run: | cd /opt sudo wget ${{ matrix.wabt_release }} - sudo tar -xzf wabt-1.0.24-*.tar.gz - sudo mv wabt-1.0.24 wabt + sudo tar -xzf wabt-1.0.31-*.tar.gz + sudo mv wabt-1.0.31 wabt + + - name: build wasi-libc (needed for wasi-threads) + run: | + mkdir wasi-libc + cd wasi-libc + git init + # "Rename thread_spawn import" commit on main branch + git fetch https://github.com/WebAssembly/wasi-libc \ + 8f5275796a82f8ecfd0833a4f3f444fa37ed4546 + git checkout FETCH_HEAD + make -j \ + AR=/opt/wasi-sdk/bin/llvm-ar \ + NM=/opt/wasi-sdk/bin/llvm-nm \ + CC=/opt/wasi-sdk/bin/clang \ + THREAD_MODEL=posix + working-directory: core/deps - name: Build Sample [basic] run: | @@ -376,20 +427,42 @@ jobs: exit $? working-directory: ./samples/simple - spec_test: - needs: [build_iwasm, build_llvm_libraries, build_wamrc] + - name: Build Sample [wasi-threads] + run: | + cd samples/wasi-threads + mkdir build && cd build + cmake -DWASI_SYSROOT=`pwd`/../../../core/deps/wasi-libc/sysroot .. + cmake --build . --config Release --parallel 4 + ./iwasm wasm-apps/no_pthread.wasm + + test: + needs: [build_iwasm, build_llvm_libraries_on_ubuntu_2004, build_wamrc] runs-on: ubuntu-20.04 strategy: matrix: running_mode: - ["classic-interp", "fast-interp", "jit", "aot", "fast-jit"] + [ + "classic-interp", + "fast-interp", + "jit", + "aot", + "fast-jit", + "multi-tier-jit", + ] test_option: [ $DEFAULT_TEST_OPTIONS, $MULTI_MODULES_TEST_OPTIONS, $SIMD_TEST_OPTIONS, $THREADS_TEST_OPTIONS, + $WASI_TEST_OPTIONS, ] + wasi_sdk_release: + [ + "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-19/wasi-sdk-19.0-linux.tar.gz", + ] + llvm_cache_key: + ["${{ needs.build_llvm_libraries_on_ubuntu_2004.outputs.cache_key }}"] exclude: # uncompatiable modes and features # classic-interp and fast-interp don't support simd @@ -402,31 +475,68 @@ jobs: test_option: $MULTI_MODULES_TEST_OPTIONS - running_mode: "jit" test_option: $MULTI_MODULES_TEST_OPTIONS - # fast-jit is only tested on default mode, exclude other three + # fast-jit doesn't support multi module, simd, and threads - running_mode: "fast-jit" test_option: $MULTI_MODULES_TEST_OPTIONS - running_mode: "fast-jit" test_option: $SIMD_TEST_OPTIONS - running_mode: "fast-jit" test_option: $THREADS_TEST_OPTIONS + - running_mode: "fast-jit" + test_option: $WASI_TEST_OPTIONS + # multi-tier-jit doesn't support multi module, simd, and threads + - running_mode: "multi-tier-jit" + test_option: $MULTI_MODULES_TEST_OPTIONS + - running_mode: "multi-tier-jit" + test_option: $SIMD_TEST_OPTIONS + - running_mode: "multi-tier-jit" + test_option: $THREADS_TEST_OPTIONS + - running_mode: "multi-tier-jit" + test_option: $WASI_TEST_OPTIONS steps: - name: checkout uses: actions/checkout@v3 + - name: download and install wasi-sdk + if: matrix.test_option == '$WASI_TEST_OPTIONS' + run: | + cd /opt + sudo wget ${{ matrix.wasi_sdk_release }} + sudo tar -xzf wasi-sdk-*.tar.gz + sudo mv wasi-sdk-19.0 wasi-sdk + + - name: build wasi-libc (needed for wasi-threads) + if: matrix.test_option == '$WASI_TEST_OPTIONS' + run: | + mkdir wasi-libc + cd wasi-libc + git init + # "Rename thread_spawn import" commit on main branch + git fetch https://github.com/WebAssembly/wasi-libc \ + 8f5275796a82f8ecfd0833a4f3f444fa37ed4546 + git checkout FETCH_HEAD + make \ + AR=/opt/wasi-sdk/bin/llvm-ar \ + NM=/opt/wasi-sdk/bin/llvm-nm \ + CC=/opt/wasi-sdk/bin/clang \ + THREAD_MODEL=posix + working-directory: core/deps + - name: set env variable(if llvm are used) - if: matrix.running_mode == 'aot' || matrix.running_mode == 'jit' + if: matrix.running_mode == 'aot' || matrix.running_mode == 'jit' || matrix.running_mode == 'multi-tier-jit' run: echo "USE_LLVM=true" >> $GITHUB_ENV - name: set env variable(if x86_32 test needed) if: > - (matrix.test_option == '$DEFAULT_TEST_OPTIONS' || matrix.test_option == '$THREADS_TEST_OPTIONS') - && matrix.running_mode != 'fast-jit' && matrix.running_mode != 'jit' + (matrix.test_option == '$DEFAULT_TEST_OPTIONS' || matrix.test_option == '$THREADS_TEST_OPTIONS' + || matrix.test_option == '$WASI_TEST_OPTIONS') + && matrix.running_mode != 'fast-jit' && matrix.running_mode != 'jit' && matrix.running_mode != 'multi-tier-jit' run: echo "TEST_ON_X86_32=true" >> $GITHUB_ENV #only download llvm libraries in jit and aot mode - name: Get LLVM libraries if: env.USE_LLVM == 'true' - id: cache_llvm + id: retrieve_llvm_libs uses: actions/cache@v3 with: path: | @@ -435,13 +545,28 @@ jobs: ./core/deps/llvm/build/lib ./core/deps/llvm/build/libexec ./core/deps/llvm/build/share - key: ubuntu-20.04-${{ env.LLVM_CACHE_SUFFIX }} + key: ${{ matrix.llvm_cache_key }} - name: Quit if cache miss - if: env.USE_LLVM == 'true' && steps.cache_llvm.outputs.cache-hit != 'true' + if: env.USE_LLVM == 'true' && steps.retrieve_llvm_libs.outputs.cache-hit != 'true' run: echo "::error::can not get prebuilt llvm libraries" && exit 1 - - name: run spec tests default and extra + - name: install jq JSON processor + if: matrix.running_mode == 'aot' && matrix.test_option == '$WASI_TEST_OPTIONS' + run: sudo apt-get update && sudo apt install -y jq + + - name: Build WASI thread tests + if: matrix.test_option == '$WASI_TEST_OPTIONS' + run: WASI_SYSROOT=../../../../../core/deps/wasi-libc/sysroot bash build.sh + working-directory: ./core/iwasm/libraries/lib-wasi-threads/test/ + + - name: build socket api tests + if: matrix.test_option == '$WASI_TEST_OPTIONS' + run: WASI_SYSROOT=../../../../../core/deps/wasi-libc/sysroot bash build.sh + working-directory: ./core/iwasm/libraries/lib-socket/test/ + + - name: run tests + timeout-minutes: 10 run: ./test_wamr.sh ${{ matrix.test_option }} -t ${{ matrix.running_mode }} working-directory: ./tests/wamr-test-suites @@ -456,7 +581,8 @@ jobs: sudo apt-get update && sudo apt install -y g++-multilib lib32gcc-9-dev - - name: run spec tests x86_32 + - name: run tests x86_32 + timeout-minutes: 10 if: env.TEST_ON_X86_32 == 'true' run: ./test_wamr.sh ${{ env.X86_32_TARGET_TEST_OPTIONS }} ${{ matrix.test_option }} -t ${{ matrix.running_mode }} working-directory: ./tests/wamr-test-suites diff --git a/.github/workflows/compilation_on_macos.yml b/.github/workflows/compilation_on_macos.yml index 73ebec892..816e65ce9 100644 --- a/.github/workflows/compilation_on_macos.yml +++ b/.github/workflows/compilation_on_macos.yml @@ -51,26 +51,28 @@ env: FAST_INTERP_BUILD_OPTIONS: "-DWAMR_BUILD_AOT=0 -DWAMR_BUILD_FAST_INTERP=1 -DWAMR_BUILD_INTERP=1 -DWAMR_BUILD_JIT=0 -DWAMR_BUILD_LAZY_JIT=0" LLVM_LAZY_JIT_BUILD_OPTIONS: "-DWAMR_BUILD_AOT=1 -DWAMR_BUILD_FAST_INTERP=0 -DWAMR_BUILD_INTERP=0 -DWAMR_BUILD_JIT=1 -DWAMR_BUILD_LAZY_JIT=1" LLVM_EAGER_JIT_BUILD_OPTIONS: "-DWAMR_BUILD_AOT=1 -DWAMR_BUILD_FAST_INTERP=0 -DWAMR_BUILD_INTERP=0 -DWAMR_BUILD_JIT=1 -DWAMR_BUILD_LAZY_JIT=0" - LLVM_CACHE_SUFFIX: "build-llvm_libraries_ex" jobs: build_llvm_libraries: uses: ./.github/workflows/build_llvm_libraries.yml with: - runs-on: "['macos-latest']" + os: "macos-latest" + arch: "X86" build_wamrc: needs: [build_llvm_libraries] runs-on: ${{ matrix.os }} strategy: matrix: - os: [macos-latest] + include: + - os: macos-latest + llvm_cache_key: ${{ needs.build_llvm_libraries.outputs.cache_key }} steps: - name: checkout uses: actions/checkout@v3 - name: Get LLVM libraries - id: cache_llvm + id: retrieve_llvm_libs uses: actions/cache@v3 with: path: | @@ -79,10 +81,10 @@ jobs: ./core/deps/llvm/build/lib ./core/deps/llvm/build/libexec ./core/deps/llvm/build/share - key: ${{ matrix.os }}-${{ env.LLVM_CACHE_SUFFIX }} + key: ${{ matrix.llvm_cache_key }} - name: Quit if cache miss - if: steps.cache_llvm.outputs.cache-hit != 'true' + if: steps.retrieve_llvm_libs.outputs.cache-hit != 'true' run: echo "::error::can not get prebuilt llvm libraries" && exit 1 - name: Build wamrc @@ -166,13 +168,16 @@ jobs: make_options_feature: "-DWAMR_BUILD_MINI_LOADER=1" - make_options_run_mode: $LLVM_EAGER_JIT_BUILD_OPTIONS make_options_feature: "-DWAMR_BUILD_MINI_LOADER=1" + include: + - os: macos-latest + llvm_cache_key: ${{ needs.build_llvm_libraries.outputs.cache_key }} steps: - name: checkout uses: actions/checkout@v3 # only download llvm cache when needed - name: Get LLVM libraries - id: cache_llvm + id: retrieve_llvm_libs if: endsWith(matrix.make_options_run_mode, '_JIT_BUILD_OPTIONS') uses: actions/cache@v3 with: @@ -182,10 +187,10 @@ jobs: ./core/deps/llvm/build/lib ./core/deps/llvm/build/libexec ./core/deps/llvm/build/share - key: ${{ matrix.os }}-${{ env.LLVM_CACHE_SUFFIX }} + key: ${{ matrix.llvm_cache_key }} - name: Quit if cache miss - if: endsWith(matrix.make_options_run_mode, '_JIT_BUILD_OPTIONS') && (steps.cache_llvm.outputs.cache-hit != 'true') + if: endsWith(matrix.make_options_run_mode, '_JIT_BUILD_OPTIONS') && (steps.retrieve_llvm_libs.outputs.cache-hit != 'true') run: echo "::error::can not get prebuilt llvm libraries" && exit 1 - name: Build iwasm @@ -210,8 +215,14 @@ jobs: #$AOT_BUILD_OPTIONS, ] os: [macos-latest] - wasi_sdk_release: ["https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-12/wasi-sdk-12.0-macos.tar.gz"] - wabt_release: ["https://github.com/WebAssembly/wabt/releases/download/1.0.24/wabt-1.0.24-macos.tar.gz"] + wasi_sdk_release: + [ + "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-19/wasi-sdk-19.0-macos.tar.gz", + ] + wabt_release: + [ + "https://github.com/WebAssembly/wabt/releases/download/1.0.31/wabt-1.0.31-macos-12.tar.gz", + ] steps: - name: checkout uses: actions/checkout@v3 @@ -220,24 +231,14 @@ jobs: run: | cd /opt sudo wget ${{ matrix.wabt_release }} - sudo tar -xzf wabt-1.0.24-*.tar.gz - sudo mv wabt-1.0.24 wabt + sudo tar -xzf wabt-1.0.31-*.tar.gz + sudo mv wabt-1.0.31 wabt - name: Build Sample [wasm-c-api] run: | - mkdir build && cd build - cmake .. ${{ matrix.make_options }} - cmake --build . --config Release --parallel 4 - ./callback - ./callback_chain - ./empty_imports - ./global - ./hello - ./hostref - ./memory - ./reflect - ./table - ./trap + cmake -S . -B build ${{ matrix.make_options }} + cmake --build build --config Release --parallel 4 + ctest --test-dir build working-directory: samples/wasm-c-api build_samples_others: @@ -246,8 +247,14 @@ jobs: strategy: matrix: os: [macos-latest] - wasi_sdk_release: ["https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-12/wasi-sdk-12.0-macos.tar.gz"] - wabt_release: ["https://github.com/WebAssembly/wabt/releases/download/1.0.24/wabt-1.0.24-macos.tar.gz"] + wasi_sdk_release: + [ + "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-19/wasi-sdk-19.0-macos.tar.gz", + ] + wabt_release: + [ + "https://github.com/WebAssembly/wabt/releases/download/1.0.31/wabt-1.0.31-macos-12.tar.gz", + ] steps: - name: checkout uses: actions/checkout@v3 @@ -256,15 +263,31 @@ jobs: run: | cd /opt sudo wget ${{ matrix.wasi_sdk_release }} - sudo tar -xzf wasi-sdk-12.0-*.tar.gz - sudo mv wasi-sdk-12.0 wasi-sdk + sudo tar -xzf wasi-sdk-*.tar.gz + sudo mv wasi-sdk-19.0 wasi-sdk - name: download and install wabt run: | cd /opt sudo wget ${{ matrix.wabt_release }} - sudo tar -xzf wabt-1.0.24-*.tar.gz - sudo mv wabt-1.0.24 wabt + sudo tar -xzf wabt-1.0.31-*.tar.gz + sudo mv wabt-1.0.31 wabt + + - name: build wasi-libc (needed for wasi-threads) + run: | + mkdir wasi-libc + cd wasi-libc + git init + # "Rename thread_spawn import" commit on main branch + git fetch https://github.com/WebAssembly/wasi-libc \ + 8f5275796a82f8ecfd0833a4f3f444fa37ed4546 + git checkout FETCH_HEAD + make \ + AR=/opt/wasi-sdk/bin/llvm-ar \ + NM=/opt/wasi-sdk/bin/llvm-nm \ + CC=/opt/wasi-sdk/bin/clang \ + THREAD_MODEL=posix + working-directory: core/deps - name: Build Sample [basic] run: | @@ -311,3 +334,11 @@ jobs: cmake .. cmake --build . --config Release --parallel 4 ./hello + + - name: Build Sample [wasi-threads] + run: | + cd samples/wasi-threads + mkdir build && cd build + cmake -DWASI_SYSROOT=`pwd`/../../../core/deps/wasi-libc/sysroot .. + cmake --build . --config Release --parallel 4 + ./iwasm wasm-apps/no_pthread.wasm diff --git a/.github/workflows/compilation_on_nuttx.yml b/.github/workflows/compilation_on_nuttx.yml index 7df9e87ee..c8553a123 100644 --- a/.github/workflows/compilation_on_nuttx.yml +++ b/.github/workflows/compilation_on_nuttx.yml @@ -45,6 +45,9 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +env: + WASI_SDK_PATH: "/opt/wasi-sdk" + jobs: build_iwasm_on_nuttx: runs-on: ubuntu-22.04 @@ -65,9 +68,12 @@ jobs: "boards/risc-v/k210/maix-bit/configs/nsh", ] wamr_config_option: [ - "CONFIG_INTERPRETERS_WAMR=y\\nCONFIG_INTERPRETERS_WAMR_AOT=y\\nCONFIG_INTERPRETERS_WAMR_FAST=y\\nCONFIG_INTERPRETERS_WAMR_LIBC_BUILTIN=y\\n", "CONFIG_INTERPRETERS_WAMR=y\\nCONFIG_INTERPRETERS_WAMR_AOT=y\\nCONFIG_INTERPRETERS_WAMR_FAST=y\\n", + "CONFIG_INTERPRETERS_WAMR=y\\nCONFIG_INTERPRETERS_WAMR_AOT=y\\nCONFIG_INTERPRETERS_WAMR_FAST=y\\nCONFIG_INTERPRETERS_WAMR_LIBC_WASI=y\\n", + "CONFIG_INTERPRETERS_WAMR=y\\nCONFIG_INTERPRETERS_WAMR_AOT=y\\nCONFIG_INTERPRETERS_WAMR_FAST=y\\nCONFIG_INTERPRETERS_WAMR_LIBC_BUILTIN=y\\n", "CONFIG_INTERPRETERS_WAMR=y\\nCONFIG_INTERPRETERS_WAMR_AOT=y\\nCONFIG_INTERPRETERS_WAMR_CLASSIC=y\\n", + "CONFIG_INTERPRETERS_WAMR=y\\nCONFIG_INTERPRETERS_WAMR_AOT=y\\nCONFIG_INTERPRETERS_WAMR_CLASSIC=y\\nCONFIG_INTERPRETERS_WAMR_LIBC_WASI=y\\n", + "CONFIG_INTERPRETERS_WAMR=y\\nCONFIG_INTERPRETERS_WAMR_AOT=y\\nCONFIG_INTERPRETERS_WAMR_CLASSIC=y\\nCONFIG_INTERPRETERS_WAMR_LIBC_BUILTIN=y\\n", "CONFIG_INTERPRETERS_WAMR=y\\nCONFIG_INTERPRETERS_WAMR_AOT=y\\nCONFIG_INTERPRETERS_WAMR_LIBC_BUILTIN=y\\n", "CONFIG_INTERPRETERS_WAMR=y\\nCONFIG_INTERPRETERS_WAMR_AOT=y\\n", "CONFIG_INTERPRETERS_WAMR=y\\nCONFIG_INTERPRETERS_WAMR_FAST=y\\n", @@ -92,6 +98,12 @@ jobs: tar xvf riscv.tar.gz echo "$PWD/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14/bin" >> $GITHUB_PATH + - name: Install WASI-SDK + run: | + curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-19/wasi-sdk-19.0-linux.tar.gz > wasi-sdk.tar.gz + tar xvf wasi-sdk.tar.gz + sudo mv wasi-sdk-* /opt/wasi-sdk + - name: Checkout NuttX uses: actions/checkout@v3 with: @@ -112,7 +124,7 @@ jobs: - name: Enable WAMR for NuttX run: | - find nuttx/boards -name defconfig | xargs sed -i '$a\CONFIG_EOL_IS_LF=y\n${{ matrix.wamr_config_option }}' + find nuttx/boards -name defconfig | xargs sed -i '$a\CONFIG_EOL_IS_LF=y\nCONFIG_PSEUDOFS_SOFTLINKS=y\n${{ matrix.wamr_config_option }}' find nuttx/boards/sim -name defconfig | xargs sed -i '$a\CONFIG_LIBM=y\n' - name: Build diff --git a/.github/workflows/compilation_on_sgx.yml b/.github/workflows/compilation_on_sgx.yml index 70eeaec02..2988a6fe1 100644 --- a/.github/workflows/compilation_on_sgx.yml +++ b/.github/workflows/compilation_on_sgx.yml @@ -51,13 +51,13 @@ env: FAST_INTERP_BUILD_OPTIONS: "-DWAMR_BUILD_AOT=0 -DWAMR_BUILD_FAST_INTERP=1 -DWAMR_BUILD_INTERP=1 -DWAMR_BUILD_JIT=0 -DWAMR_BUILD_LAZY_JIT=0" LLVM_LAZY_JIT_BUILD_OPTIONS: "-DWAMR_BUILD_AOT=1 -DWAMR_BUILD_FAST_INTERP=0 -DWAMR_BUILD_INTERP=0 -DWAMR_BUILD_JIT=1 -DWAMR_BUILD_LAZY_JIT=1" LLVM_EAGER_JIT_BUILD_OPTIONS: "-DWAMR_BUILD_AOT=1 -DWAMR_BUILD_FAST_INTERP=0 -DWAMR_BUILD_INTERP=0 -DWAMR_BUILD_JIT=1 -DWAMR_BUILD_LAZY_JIT=0" - LLVM_CACHE_SUFFIX: "build-llvm_libraries_ex" jobs: build_llvm_libraries: uses: ./.github/workflows/build_llvm_libraries.yml with: - runs-on: "['ubuntu-20.04']" + os: "ubuntu-20.04" + arch: "X86" build_iwasm: runs-on: ${{ matrix.os }} @@ -131,7 +131,9 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-20.04] + include: + - os: ubuntu-20.04 + llvm_cache_key: ${{ needs.build_llvm_libraries.outputs.cache_key }} steps: - name: install SGX SDK and necessary libraries run: | @@ -150,7 +152,7 @@ jobs: uses: actions/checkout@v3 - name: Get LLVM libraries - id: cache_llvm + id: retrieve_llvm_libs uses: actions/cache@v3 with: path: | @@ -159,10 +161,10 @@ jobs: ./core/deps/llvm/build/lib ./core/deps/llvm/build/libexec ./core/deps/llvm/build/share - key: ${{ matrix.os }}-${{ env.LLVM_CACHE_SUFFIX }} + key: ${{ matrix.llvm_cache_key }} - name: Quit if cache miss - if: steps.cache_llvm.outputs.cache-hit != 'true' + if: steps.retrieve_llvm_libs.outputs.cache-hit != 'true' run: echo "::error::can not get prebuilt llvm libraries" && exit 1 - name: Build wamrc @@ -189,11 +191,11 @@ jobs: os: [ubuntu-20.04] wasi_sdk_release: [ - "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-12/wasi-sdk-12.0-linux.tar.gz", + "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-19/wasi-sdk-19.0-linux.tar.gz", ] wabt_release: [ - "https://github.com/WebAssembly/wabt/releases/download/1.0.24/wabt-1.0.24-ubuntu.tar.gz", + "https://github.com/WebAssembly/wabt/releases/download/1.0.31/wabt-1.0.31-ubuntu.tar.gz", ] steps: - name: checkout @@ -203,8 +205,8 @@ jobs: run: | cd /opt sudo wget ${{ matrix.wabt_release }} - sudo tar -xzf wabt-1.0.24-*.tar.gz - sudo mv wabt-1.0.24 wabt + sudo tar -xzf wabt-1.0.31-*.tar.gz + sudo mv wabt-1.0.31 wabt - name: install SGX SDK and necessary libraries run: | @@ -221,19 +223,9 @@ jobs: - name: Build Sample [wasm-c-api] run: | - mkdir build && cd build - cmake .. ${{ matrix.make_options }} - cmake --build . --config Release --parallel 4 - ./callback - ./callback_chain - ./empty_imports - ./global - ./hello - ./hostref - ./memory - ./reflect - ./table - ./trap + cmake -S . -B build ${{ matrix.make_options }} + cmake --build build --config Release --parallel 4 + ctest --test-dir build working-directory: samples/wasm-c-api build_samples_others: @@ -244,11 +236,11 @@ jobs: os: [ubuntu-20.04] wasi_sdk_release: [ - "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-12/wasi-sdk-12.0-linux.tar.gz", + "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-19/wasi-sdk-19.0-linux.tar.gz", ] wabt_release: [ - "https://github.com/WebAssembly/wabt/releases/download/1.0.24/wabt-1.0.24-ubuntu.tar.gz", + "https://github.com/WebAssembly/wabt/releases/download/1.0.31/wabt-1.0.31-ubuntu.tar.gz", ] steps: - name: checkout @@ -258,15 +250,31 @@ jobs: run: | cd /opt sudo wget ${{ matrix.wasi_sdk_release }} - sudo tar -xzf wasi-sdk-12.0-*.tar.gz - sudo mv wasi-sdk-12.0 wasi-sdk + sudo tar -xzf wasi-sdk-*.tar.gz + sudo mv wasi-sdk-19.0 wasi-sdk - name: download and install wabt run: | cd /opt sudo wget ${{ matrix.wabt_release }} - sudo tar -xzf wabt-1.0.24-*.tar.gz - sudo mv wabt-1.0.24 wabt + sudo tar -xzf wabt-1.0.31-*.tar.gz + sudo mv wabt-1.0.31 wabt + + - name: build wasi-libc (needed for wasi-threads) + run: | + mkdir wasi-libc + cd wasi-libc + git init + # "Rename thread_spawn import" commit on main branch + git fetch https://github.com/WebAssembly/wasi-libc \ + 8f5275796a82f8ecfd0833a4f3f444fa37ed4546 + git checkout FETCH_HEAD + make \ + AR=/opt/wasi-sdk/bin/llvm-ar \ + NM=/opt/wasi-sdk/bin/llvm-nm \ + CC=/opt/wasi-sdk/bin/clang \ + THREAD_MODEL=posix + working-directory: core/deps - name: install SGX SDK and necessary libraries run: | @@ -327,6 +335,14 @@ jobs: cmake --build . --config Release --parallel 4 ./hello + - name: Build Sample [wasi-threads] + run: | + cd samples/wasi-threads + mkdir build && cd build + cmake -DWASI_SYSROOT=`pwd`/../../../core/deps/wasi-libc/sysroot .. + cmake --build . --config Release --parallel 4 + ./iwasm wasm-apps/no_pthread.wasm + spec_test_default: needs: [build_iwasm, build_llvm_libraries, build_wamrc] runs-on: ubuntu-20.04 @@ -334,6 +350,7 @@ jobs: matrix: running_mode: ["classic-interp", "fast-interp", "aot"] test_option: ["-x -p -s spec -b -P", "-x -p -s spec -S -b -P"] + llvm_cache_key: ["${{ needs.build_llvm_libraries.outputs.cache_key }}"] # classic-interp and fast-interp don't support simd exclude: - running_mode: "classic-interp" @@ -347,7 +364,7 @@ jobs: - name: Get LLVM libraries if: matrix.running_mode == 'aot' - id: cache_llvm + id: retrieve_llvm_libs uses: actions/cache@v3 with: path: | @@ -356,10 +373,10 @@ jobs: ./core/deps/llvm/build/lib ./core/deps/llvm/build/libexec ./core/deps/llvm/build/share - key: ubuntu-20.04-${{ env.LLVM_CACHE_SUFFIX }} + key: ${{ matrix.llvm_cache_key }} - name: Quit if cache miss - if: matrix.running_mode == 'aot' && steps.cache_llvm.outputs.cache-hit != 'true' + if: matrix.running_mode == 'aot' && steps.retrieve_llvm_libs.outputs.cache-hit != 'true' run: echo "::error::can not get prebuilt llvm libraries" && exit 1 - name: install SGX SDK and necessary libraries diff --git a/.github/workflows/compilation_on_windows.yml b/.github/workflows/compilation_on_windows.yml index 56e913e3a..073f206ed 100644 --- a/.github/workflows/compilation_on_windows.yml +++ b/.github/workflows/compilation_on_windows.yml @@ -118,3 +118,17 @@ jobs: cmake .. -DWAMR_BUILD_DEBUG_INTERP=1 cmake --build . --config Release --parallel 4 cd .. && rm -force -r build + - name: Build iwasm [lib pthread] + run: | + cd product-mini/platforms/windows + mkdir build && cd build + cmake .. -DWAMR_BUILD_LIB_PTHREAD=1 + cmake --build . --config Release --parallel 4 + cd .. && rm -force -r build + - name: Build iwasm [lib wasi-thread] + run: | + cd product-mini/platforms/windows + mkdir build && cd build + cmake .. -DWAMR_BUILD_LIB_WASI_THREADS=1 + cmake --build . --config Release --parallel 4 + cd .. && rm -force -r build diff --git a/.github/workflows/release_process.yml b/.github/workflows/release_process.yml index aae2f890a..88f595dd1 100644 --- a/.github/workflows/release_process.yml +++ b/.github/workflows/release_process.yml @@ -123,7 +123,7 @@ jobs: runner: ubuntu-20.04 upload_url: ${{ needs.create_release.outputs.upload_url }} ver_num: ${{ needs.create_tag.outputs.new_ver}} - wasi_sdk_url: https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-12/wasi-sdk-12.0-linux.tar.gz + wasi_sdk_url: https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-19/wasi-sdk-19.0-linux.tar.gz release_wamr_sdk_on_ubuntu_2204: needs: [create_tag, create_release] @@ -133,7 +133,7 @@ jobs: runner: ubuntu-22.04 upload_url: ${{ needs.create_release.outputs.upload_url }} ver_num: ${{ needs.create_tag.outputs.new_ver}} - wasi_sdk_url: https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-12/wasi-sdk-12.0-linux.tar.gz + wasi_sdk_url: https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-19/wasi-sdk-19.0-linux.tar.gz release_wamr_sdk_on_macos: needs: [create_tag, create_release] @@ -143,13 +143,14 @@ jobs: runner: macos-latest upload_url: ${{ needs.create_release.outputs.upload_url }} ver_num: ${{ needs.create_tag.outputs.new_ver}} - wasi_sdk_url: https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-12/wasi-sdk-12.0-macos.tar.gz + wasi_sdk_url: https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-19/wasi-sdk-19.0-macos.tar.gz # # vscode extension cross-platform release_wamr_ide_vscode_ext: needs: [create_tag, create_release] uses: ./.github/workflows/build_wamr_vscode_ext.yml + secrets: inherit with: upload_url: ${{ needs.create_release.outputs.upload_url }} ver_num: ${{ needs.create_tag.outputs.new_ver }} diff --git a/.github/workflows/spec_test_on_nuttx.yml b/.github/workflows/spec_test_on_nuttx.yml index 03f99f73a..42d3968b8 100644 --- a/.github/workflows/spec_test_on_nuttx.yml +++ b/.github/workflows/spec_test_on_nuttx.yml @@ -11,12 +11,14 @@ on: env: LLVM_CACHE_SUFFIX: "build-llvm_libraries_ex" + WASI_SDK_PATH: "/opt/wasi-sdk" jobs: build_llvm_libraries: uses: ./.github/workflows/build_llvm_libraries.yml with: - runs-on: "['ubuntu-22.04']" + os: "ubuntu-22.04" + arch: "ARM RISCV AArch64" spec_test_on_qemu: runs-on: ${{ matrix.os }} @@ -37,6 +39,7 @@ jobs: "-t aot", "-t aot -X" ] + llvm_cache_key: [ "${{ needs.build_llvm_libraries.outputs.cache_key }}" ] steps: - name: Install Utilities run: | @@ -53,6 +56,12 @@ jobs: tar xvf riscv.tar.gz echo "$PWD/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14/bin" >> $GITHUB_PATH + - name: Install WASI-SDK + run: | + curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-19/wasi-sdk-19.0-linux.tar.gz > wasi-sdk.tar.gz + tar xvf wasi-sdk.tar.gz + sudo mv wasi-sdk-* /opt/wasi-sdk + - name: Checkout NuttX uses: actions/checkout@v3 with: @@ -72,7 +81,7 @@ jobs: path: apps/interpreters/wamr/wamr - name: Get LLVM libraries - id: cache_llvm + id: retrieve_llvm_libs uses: actions/cache@v3 with: path: | @@ -81,10 +90,10 @@ jobs: ./core/deps/llvm/build/lib ./core/deps/llvm/build/libexec ./core/deps/llvm/build/share - key: ${{ matrix.os }}-${{ env.LLVM_CACHE_SUFFIX }} + key: ${{ matrix.llvm_cache_key }} - name: Quit if cache miss - if: steps.cache_llvm.outputs.cache-hit != 'true' + if: steps.retrieve_llvm_libs.outputs.cache-hit != 'true' run: echo "::error::can not get prebuilt llvm libraries" && exit 1 - name: Copy LLVM diff --git a/.gitignore b/.gitignore index a7abd7c14..a4889fb7f 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,8 @@ core/deps/** core/shared/mem-alloc/tlsf core/app-framework/wgl +core/iwasm/libraries/lib-wasi-threads/test/*.wasm +core/iwasm/libraries/lib-socket/test/*.wasm wamr-sdk/out/ wamr-sdk/runtime/build_runtime_sdk/ @@ -32,3 +34,8 @@ samples/socket-api/wasm-src/inc/pthread.h **/__pycache__ tests/benchmarks/coremark/coremark* + +samples/workload/include/** +!samples/workload/include/.gitkeep + +# core/iwasm/libraries/wasi-threads \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 49b2969a4..1c8799494 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,9 @@ project (iwasm) set (CMAKE_VERBOSE_MAKEFILE OFF) -string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM) +if (NOT DEFINED WAMR_BUILD_PLATFORM) + string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM) +endif () # Reset default linker flags set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") @@ -83,6 +85,11 @@ if (NOT DEFINED WAMR_BUILD_LIB_PTHREAD) set (WAMR_BUILD_LIB_PTHREAD 0) endif () +if (NOT DEFINED WAMR_BUILD_LIB_WASI_THREADS) + # Disable wasi threads library by default + set (WAMR_BUILD_LIB_WASI_THREADS 0) +endif () + if (NOT DEFINED WAMR_BUILD_MINI_LOADER) # Disable wasm mini loader by default set (WAMR_BUILD_MINI_LOADER 0) @@ -98,11 +105,6 @@ if (NOT DEFINED WAMR_BUILD_REF_TYPES) set (WAMR_BUILD_REF_TYPES 0) endif () -if (COLLECT_CODE_COVERAGE EQUAL 1) - set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage") - set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") -endif () - set (WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}) include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) @@ -130,23 +132,26 @@ include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) # STATIC LIBRARY add_library(iwasm_static STATIC ${WAMR_RUNTIME_LIB_SOURCE}) -if (WAMR_BUILD_WASM_CACHE EQUAL 1) - target_link_libraries(iwasm_static PUBLIC boringssl_crypto) -endif () set_target_properties (iwasm_static PROPERTIES OUTPUT_NAME vmlib) +target_include_directories(iwasm_static INTERFACE ${WAMR_ROOT_DIR}/core/iwasm/include) +target_link_libraries (iwasm_static INTERFACE ${LLVM_AVAILABLE_LIBS} ${UV_A_LIBS} -lm -ldl -lpthread) +if (WAMR_BUILD_WASM_CACHE EQUAL 1) + target_link_libraries(iwasm_static INTERFACE boringssl_crypto) +endif () install (TARGETS iwasm_static ARCHIVE DESTINATION lib) # SHARED LIBRARY add_library (iwasm_shared SHARED ${WAMR_RUNTIME_LIB_SOURCE}) set_target_properties (iwasm_shared PROPERTIES OUTPUT_NAME iwasm) -target_link_libraries (iwasm_shared ${LLVM_AVAILABLE_LIBS} ${UV_A_LIBS} -lm -ldl -lpthread) +target_include_directories(iwasm_shared INTERFACE ${WAMR_ROOT_DIR}/core/iwasm/include) +target_link_libraries (iwasm_shared INTERFACE ${LLVM_AVAILABLE_LIBS} ${UV_A_LIBS} -lm -ldl -lpthread) if (WAMR_BUILD_WASM_CACHE EQUAL 1) - target_link_libraries(iwasm_shared boringssl_crypto) + target_link_libraries(iwasm_shared INTERFACE boringssl_crypto) endif () if (MINGW) -target_link_libraries (iwasm_shared -lWs2_32) + target_link_libraries (iwasm_shared -lWs2_32) endif () install (TARGETS iwasm_shared LIBRARY DESTINATION lib) @@ -155,4 +160,5 @@ install (TARGETS iwasm_shared LIBRARY DESTINATION lib) install (FILES ${WAMR_ROOT_DIR}/core/iwasm/include/wasm_c_api.h ${WAMR_ROOT_DIR}/core/iwasm/include/wasm_export.h + ${WAMR_ROOT_DIR}/core/iwasm/include/lib_export.h DESTINATION include) diff --git a/build-scripts/build_llvm.py b/build-scripts/build_llvm.py index bc0daf1f7..3957f4b89 100755 --- a/build-scripts/build_llvm.py +++ b/build-scripts/build_llvm.py @@ -7,6 +7,7 @@ import argparse import os import pathlib +import requests import shlex import shutil import subprocess @@ -21,28 +22,44 @@ def clone_llvm(dst_dir, llvm_repo, llvm_branch): llvm_dir = dst_dir.joinpath("llvm").resolve() if not llvm_dir.exists(): - print(f"Clone llvm to {llvm_dir} ...") GIT_CLONE_CMD = f"git clone --depth 1 --branch {llvm_branch} {llvm_repo} llvm" + print(GIT_CLONE_CMD) subprocess.check_output(shlex.split(GIT_CLONE_CMD), cwd=dst_dir) - else: - print(f"There is an LLVM local repo in {llvm_dir}, clean and keep using it") return llvm_dir -def build_llvm(llvm_dir, platform, backends, projects): +def query_llvm_version(llvm_info): + github_token = os.environ['GH_TOKEN'] + owner_project = llvm_info['repo'].replace("https://github.com/", "").replace(".git", "") + url = f"https://api.github.com/repos/{owner_project}/commits/{llvm_info['branch']}" + headers = { + 'Authorization': f"Bearer {github_token}" + } + + try: + response = requests.request("GET", url, headers=headers, data={}) + response.raise_for_status() + except requests.exceptions.HTTPError as error: + print (error) # for debugging purpose + return None + + response = response.json() + return response['sha'] + + +def build_llvm(llvm_dir, platform, backends, projects, use_clang=False, extra_flags=''): LLVM_COMPILE_OPTIONS = [ '-DCMAKE_BUILD_TYPE:STRING="Release"', "-DCMAKE_EXPORT_COMPILE_COMMANDS=ON", "-DLLVM_APPEND_VC_REV:BOOL=ON", - "-DLLVM_BUILD_BENCHMARKS:BOOL=OFF", - "-DLLVM_BUILD_DOCS:BOOL=OFF", "-DLLVM_BUILD_EXAMPLES:BOOL=OFF", "-DLLVM_BUILD_LLVM_DYLIB:BOOL=OFF", "-DLLVM_BUILD_TESTS:BOOL=OFF", - "-DLLVM_CCACHE_BUILD:BOOL=OFF", + "-DLLVM_CCACHE_BUILD:BOOL=ON", "-DLLVM_ENABLE_BINDINGS:BOOL=OFF", "-DLLVM_ENABLE_IDE:BOOL=OFF", + "-DLLVM_ENABLE_LIBEDIT=OFF", "-DLLVM_ENABLE_TERMINFO:BOOL=OFF", "-DLLVM_ENABLE_ZLIB:BOOL=OFF", "-DLLVM_INCLUDE_BENCHMARKS:BOOL=OFF", @@ -54,6 +71,18 @@ def build_llvm(llvm_dir, platform, backends, projects): "-DLLVM_OPTIMIZED_TABLEGEN:BOOL=ON", ] + # use clang/clang++/lld. but macos doesn't support lld + if not sys.platform.startswith("darwin") and use_clang: + if shutil.which("clang") and shutil.which("clang++") and shutil.which("lld"): + os.environ["CC"] = "clang" + os.environ["CXX"] = "clang++" + LLVM_COMPILE_OPTIONS.append('-DLLVM_USE_LINKER:STRING="lld"') + print("Use the clang toolchain") + else: + print("Can not find clang, clang++ and lld, keep using the gcc toolchain") + else: + print("Use the gcc toolchain") + LLVM_EXTRA_COMPILE_OPTIONS = { "arc": [ '-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD:STRING="ARC"', @@ -99,8 +128,10 @@ def build_llvm(llvm_dir, platform, backends, projects): lib_llvm_core_library = build_dir.joinpath("lib/libLLVMCore.a").resolve() if lib_llvm_core_library.exists(): - print(f"Please remove {build_dir} manually and try again") - return build_dir + print( + f"It has already been fully compiled. If want to a re-build, please remove {build_dir} manually and try again" + ) + return None compile_options = " ".join( LLVM_COMPILE_OPTIONS @@ -113,16 +144,17 @@ def build_llvm(llvm_dir, platform, backends, projects): + LLVM_INCLUDE_TOOLS_OPTION ) - CONFIG_CMD = f"cmake {compile_options} ../llvm" + CONFIG_CMD = f"cmake {compile_options} {extra_flags} ../llvm" if "windows" == platform: if "mingw" in sysconfig.get_platform().lower(): CONFIG_CMD += " -G'Unix Makefiles'" else: CONFIG_CMD += " -A x64" - print(f"{CONFIG_CMD}") + else: + CONFIG_CMD += " -G'Ninja'" subprocess.check_call(shlex.split(CONFIG_CMD), cwd=build_dir) - BUILD_CMD = f"cmake --build . --target package --parallel {os.cpu_count()}" + ( + BUILD_CMD = "cmake --build . --target package" + ( " --config Release" if "windows" == platform else "" ) subprocess.check_call(shlex.split(BUILD_CMD), cwd=build_dir) @@ -133,23 +165,25 @@ def build_llvm(llvm_dir, platform, backends, projects): def repackage_llvm(llvm_dir): build_dir = llvm_dir.joinpath("./build").resolve() - packs = [f for f in build_dir.glob("LLVM-13*.tar.gz")] + packs = [f for f in build_dir.glob("LLVM-*.tar.gz")] if len(packs) > 1: - raise Exception("Find more than one LLVM-13*.tar.gz") + raise Exception("Find more than one LLVM-*.tar.gz") if not packs: return llvm_package = packs[0].name - # mv build/LLVM-13.0.0*.gz . + # mv build/LLVM-*.gz . shutil.move(str(build_dir.joinpath(llvm_package).resolve()), str(llvm_dir)) # rm -r build shutil.rmtree(str(build_dir)) # mkdir build build_dir.mkdir() - # tar xf ./LLVM-13.0.0-*.tar.gz --strip-components=1 --directory=build + # tar xf ./LLVM-*.tar.gz --strip-components=1 --directory=build CMD = f"tar xf {llvm_dir.joinpath(llvm_package).resolve()} --strip-components=1 --directory={build_dir}" subprocess.check_call(shlex.split(CMD), cwd=llvm_dir) + # rm ./LLVM-1*.gz + os.remove(llvm_dir.joinpath(llvm_package).resolve()) def main(): @@ -184,8 +218,23 @@ def main(): choices=["clang", "lldb"], help="identify extra LLVM projects, separate by space, like '--project clang lldb'", ) + parser.add_argument( + "--llvm-ver", + action="store_true", + help="return the version info of generated llvm libraries", + ) + parser.add_argument( + "--use-clang", + action="store_true", + help="use clang instead of gcc", + ) + parser.add_argument( + "--extra-cmake-flags", + type=str, + default="", + help="custom extra cmake flags", + ) options = parser.parse_args() - print(f"options={options}") # if the "platform" is not identified in the command line option, # detect it @@ -199,20 +248,21 @@ def main(): else: platform = options.platform - print(f"========== Build LLVM for {platform} ==========\n") - llvm_repo_and_branch = { "arc": { "repo": "https://github.com/llvm/llvm-project.git", - "branch": "release/13.x", + "repo_ssh": "git@github.com:llvm/llvm-project.git", + "branch": "release/15.x", }, "xtensa": { - "repo": "https://github.com/espressif/llvm-project.git", + "repo": "https://github.com/espressif/llvm-project.git", + "repo_ssh": "git@github.com:espressif/llvm-project.git", "branch": "xtensa_release_15.x", }, "default": { "repo": "https://github.com/llvm/llvm-project.git", - "branch": "release/13.x", + "repo_ssh": "git@github.com:llvm/llvm-project.git", + "branch": "release/15.x", }, } @@ -225,19 +275,29 @@ def main(): deps_dir = current_dir.joinpath("../core/deps").resolve() try: - print(f"==================== CLONE LLVM ====================") llvm_info = llvm_repo_and_branch.get(platform, llvm_repo_and_branch["default"]) - llvm_dir = clone_llvm(deps_dir, llvm_info["repo"], llvm_info["branch"]) - print() - print(f"==================== BUILD LLVM ====================") - build_llvm(llvm_dir, platform, options.arch, options.project) + if options.llvm_ver: + commit_hash = query_llvm_version(llvm_info) + print(commit_hash) + return commit_hash is not None + + repo_addr = llvm_info["repo"] + if os.environ.get('USE_GIT_SSH') == "true": + repo_addr = llvm_info["repo_ssh"] + else: + print("To use ssh for git clone, run: export USE_GIT_SSH=true") + + llvm_dir = clone_llvm(deps_dir, repo_addr, llvm_info["branch"]) + if ( + build_llvm( + llvm_dir, platform, options.arch, options.project, options.use_clang, + options.extra_cmake_flags + ) + is not None + ): + repackage_llvm(llvm_dir) - print() - print(f"==================== PACKAGE LLVM ====================") - repackage_llvm(llvm_dir) - - print() return True except subprocess.CalledProcessError: return False diff --git a/build-scripts/config_common.cmake b/build-scripts/config_common.cmake index ad074e64b..ae4ec129e 100644 --- a/build-scripts/config_common.cmake +++ b/build-scripts/config_common.cmake @@ -337,6 +337,11 @@ if (WAMR_BUILD_SGX_IPFS EQUAL 1) endif () if (WAMR_BUILD_WASI_NN EQUAL 1) message (" WASI-NN enabled") + add_definitions (-DWASM_ENABLE_WASI_NN=1) + if (WASI_NN_ENABLE_GPU EQUAL 1) + message (" WASI-NN: GPU enabled") + add_definitions (-DWASI_NN_ENABLE_GPU=1) + endif () endif () if (WAMR_BUILD_ALLOC_WITH_USER_DATA EQUAL 1) add_definitions(-DWASM_MEM_ALLOC_WITH_USER_DATA=1) @@ -345,3 +350,13 @@ if (WAMR_BUILD_WASM_CACHE EQUAL 1) add_definitions (-DWASM_ENABLE_WASM_CACHE=1) message (" Wasm files cache enabled") endif () +if (WAMR_BUILD_GC_HEAP_VERIFY EQUAL 1) + add_definitions (-DWASM_ENABLE_GC_VERIFY=1) + message (" GC heap verification enabled") +endif () +if ("$ENV{COLLECT_CODE_COVERAGE}" STREQUAL "1" OR COLLECT_CODE_COVERAGE EQUAL 1) + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage") + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") + add_definitions (-DCOLLECT_CODE_COVERAGE) + message (" Collect code coverage enabled") +endif () diff --git a/build-scripts/requirements.txt b/build-scripts/requirements.txt new file mode 100644 index 000000000..bf0d9d411 --- /dev/null +++ b/build-scripts/requirements.txt @@ -0,0 +1 @@ +requests==2.28.2 \ No newline at end of file diff --git a/build-scripts/runtime_lib.cmake b/build-scripts/runtime_lib.cmake index ac3f41447..80ca85c25 100644 --- a/build-scripts/runtime_lib.cmake +++ b/build-scripts/runtime_lib.cmake @@ -19,6 +19,11 @@ endif () if (NOT DEFINED DEPS_DIR) set (DEPS_DIR ${WAMR_ROOT_DIR}/core/deps) endif () +if (NOT DEFINED SHARED_PLATFORM_CONFIG) + # CMake file for platform configuration. The PLATFORM_SHARED_SOURCE varable + # should point to a list of platform-specfic source files to compile. + set (SHARED_PLATFORM_CONFIG ${SHARED_DIR}/platform/${WAMR_BUILD_PLATFORM}/shared_platform.cmake) +endif () if (DEFINED EXTRA_SDK_INCLUDE_PATH) message(STATUS, "EXTRA_SDK_INCLUDE_PATH = ${EXTRA_SDK_INCLUDE_PATH} ") @@ -96,10 +101,21 @@ if (WAMR_BUILD_LIB_PTHREAD_SEMAPHORE EQUAL 1) endif () if (WAMR_BUILD_WASI_NN EQUAL 1) - execute_process(COMMAND ${WAMR_ROOT_DIR}/core/deps/install_tensorflow.sh - RESULT_VARIABLE TENSORFLOW_RESULT - ) + if (NOT EXISTS "${WAMR_ROOT_DIR}/core/deps/tensorflow-src") + execute_process(COMMAND ${WAMR_ROOT_DIR}/core/deps/install_tensorflow.sh + RESULT_VARIABLE TENSORFLOW_RESULT + ) + else () + message("Tensorflow is already downloaded.") + endif() set(TENSORFLOW_SOURCE_DIR "${WAMR_ROOT_DIR}/core/deps/tensorflow-src") + + if (WASI_NN_ENABLE_GPU EQUAL 1) + # Tensorflow specific: + # * https://www.tensorflow.org/lite/guide/build_cmake#available_options_to_build_tensorflow_lite + set (TFLITE_ENABLE_GPU ON) + endif () + include_directories (${CMAKE_CURRENT_BINARY_DIR}/flatbuffers/include) include_directories (${TENSORFLOW_SOURCE_DIR}) add_subdirectory( @@ -116,6 +132,14 @@ if (WAMR_BUILD_LIB_PTHREAD EQUAL 1) set (WAMR_BUILD_SHARED_MEMORY 1) endif () +if (WAMR_BUILD_LIB_WASI_THREADS EQUAL 1) + include (${IWASM_DIR}/libraries/lib-wasi-threads/lib_wasi_threads.cmake) + # Enable the dependent feature if lib wasi threads is enabled + set (WAMR_BUILD_THREAD_MGR 1) + set (WAMR_BUILD_BULK_MEMORY 1) + set (WAMR_BUILD_SHARED_MEMORY 1) +endif () + if (WAMR_BUILD_DEBUG_INTERP EQUAL 1) set (WAMR_BUILD_THREAD_MGR 1) include (${IWASM_DIR}/libraries/debug-engine/debug_engine.cmake) @@ -161,7 +185,7 @@ LIST (APPEND RUNTIME_LIB_HEADER_LIST ${header}) enable_language (ASM) -include (${SHARED_DIR}/platform/${WAMR_BUILD_PLATFORM}/shared_platform.cmake) +include (${SHARED_PLATFORM_CONFIG}) include (${SHARED_DIR}/mem-alloc/mem_alloc.cmake) include (${IWASM_DIR}/common/iwasm_common.cmake) include (${SHARED_DIR}/utils/shared_utils.cmake) @@ -182,6 +206,7 @@ set (source_all ${WASM_APP_LIB_SOURCE_ALL} ${NATIVE_INTERFACE_SOURCE} ${APP_MGR_SOURCE} + ${LIB_WASI_THREADS_SOURCE} ${LIB_PTHREAD_SOURCE} ${THREAD_MGR_SOURCE} ${LIBC_EMCC_SOURCE} diff --git a/core/app-framework/app-native-shared/attr_container.c b/core/app-framework/app-native-shared/attr_container.c index e8c120196..e1e9f4e35 100644 --- a/core/app-framework/app-native-shared/attr_container.c +++ b/core/app-framework/app-native-shared/attr_container.c @@ -7,11 +7,14 @@ typedef union jvalue { bool z; - int8_t b; - uint16_t c; - int16_t s; - int32_t i; - int64_t j; + int8_t i8; + uint8_t u8; + int16_t i16; + uint16_t u16; + int32_t i32; + uint32_t u32; + int64_t i64; + uint64_t u64; float f; double d; } jvalue; @@ -27,7 +30,9 @@ get_int16(const char *buf) static inline uint16_t get_uint16(const char *buf) { - return get_int16(buf); + uint16_t ret; + bh_memcpy_s(&ret, sizeof(uint16_t), buf, sizeof(uint16_t)); + return ret; } static inline int32_t @@ -41,7 +46,9 @@ get_int32(const char *buf) static inline uint32_t get_uint32(const char *buf) { - return get_int32(buf); + uint32_t ret; + bh_memcpy_s(&ret, sizeof(uint32_t), buf, sizeof(uint32_t)); + return ret; } static inline int64_t @@ -55,7 +62,9 @@ get_int64(const char *buf) static inline uint64_t get_uint64(const char *buf) { - return get_int64(buf); + uint64_t ret; + bh_memcpy_s(&ret, sizeof(uint64_t), buf, sizeof(uint64_t)); + return ret; } static inline void @@ -145,8 +154,8 @@ attr_container_get_attr_next(const char *curr_attr) p += sizeof(uint16_t) + get_uint16(p); type = *p++; - /* Short type to Boolean type */ - if (type >= ATTR_TYPE_SHORT && type <= ATTR_TYPE_BOOLEAN) { + /* Byte type to Boolean type */ + if (type >= ATTR_TYPE_BYTE && type <= ATTR_TYPE_BOOLEAN) { p += 1 << (type & 3); return p; } @@ -342,7 +351,7 @@ attr_container_set_attr(attr_container_t **p_attr_cont, const char *key, /* key len + key + '\0' + type */ attr_len = sizeof(uint16_t) + strlen(key) + 1 + 1; - if (type >= ATTR_TYPE_SHORT && type <= ATTR_TYPE_BOOLEAN) + if (type >= ATTR_TYPE_BYTE && type <= ATTR_TYPE_BOOLEAN) attr_len += 1 << (type & 3); else if (type == ATTR_TYPE_STRING) attr_len += sizeof(uint16_t) + value_length; @@ -362,7 +371,7 @@ attr_container_set_attr(attr_container_t **p_attr_cont, const char *key, p += str_len; *p++ = type; - if (type >= ATTR_TYPE_SHORT && type <= ATTR_TYPE_BOOLEAN) + if (type >= ATTR_TYPE_BYTE && type <= ATTR_TYPE_BOOLEAN) bh_memcpy_s(p, 1 << (type & 3), value, 1 << (type & 3)); else if (type == ATTR_TYPE_STRING) { set_uint16(p, value_length); @@ -460,6 +469,14 @@ attr_container_set_short(attr_container_t **p_attr_cont, const char *key, 2); } +bool +attr_container_set_int16(attr_container_t **p_attr_cont, const char *key, + int16_t value) +{ + return attr_container_set_attr(p_attr_cont, key, ATTR_TYPE_INT16, &value, + 2); +} + bool attr_container_set_int(attr_container_t **p_attr_cont, const char *key, int value) @@ -467,6 +484,22 @@ attr_container_set_int(attr_container_t **p_attr_cont, const char *key, return attr_container_set_attr(p_attr_cont, key, ATTR_TYPE_INT, &value, 4); } +bool +attr_container_set_int32(attr_container_t **p_attr_cont, const char *key, + int32_t value) +{ + return attr_container_set_attr(p_attr_cont, key, ATTR_TYPE_INT32, &value, + 4); +} + +bool +attr_container_set_uint32(attr_container_t **p_attr_cont, const char *key, + uint32_t value) +{ + return attr_container_set_attr(p_attr_cont, key, ATTR_TYPE_UINT32, &value, + 4); +} + bool attr_container_set_int64(attr_container_t **p_attr_cont, const char *key, int64_t value) @@ -475,6 +508,14 @@ attr_container_set_int64(attr_container_t **p_attr_cont, const char *key, 8); } +bool +attr_container_set_uint64(attr_container_t **p_attr_cont, const char *key, + uint64_t value) +{ + return attr_container_set_attr(p_attr_cont, key, ATTR_TYPE_UINT64, &value, + 8); +} + bool attr_container_set_byte(attr_container_t **p_attr_cont, const char *key, int8_t value) @@ -482,6 +523,21 @@ attr_container_set_byte(attr_container_t **p_attr_cont, const char *key, return attr_container_set_attr(p_attr_cont, key, ATTR_TYPE_BYTE, &value, 1); } +bool +attr_container_set_int8(attr_container_t **p_attr_cont, const char *key, + int8_t value) +{ + return attr_container_set_attr(p_attr_cont, key, ATTR_TYPE_INT8, &value, 1); +} + +bool +attr_container_set_uint8(attr_container_t **p_attr_cont, const char *key, + uint8_t value) +{ + return attr_container_set_attr(p_attr_cont, key, ATTR_TYPE_UINT8, &value, + 1); +} + bool attr_container_set_uint16(attr_container_t **p_attr_cont, const char *key, uint16_t value) @@ -552,7 +608,7 @@ attr_container_get_attr(const attr_container_t *attr_cont, const char *key) if (!(attr_addr = attr_container_find_attr(attr_cont, key))) { attr_container_printf("Get attribute failed: lookup key failed.\r\n"); - return false; + return NULL; } /* key len + key + '\0' */ @@ -566,14 +622,17 @@ attr_container_get_attr(const attr_container_t *attr_cont, const char *key) uint8_t type; \ if (!addr) \ return 0; \ - val.j = 0; \ + val.i64 = 0; \ type = *(uint8_t *)addr++; \ switch (type) { \ - case ATTR_TYPE_SHORT: \ - case ATTR_TYPE_INT: \ + case ATTR_TYPE_BYTE: /* = ATTR_TYPE_INT8 */ \ + case ATTR_TYPE_SHORT: /* = ATTR_TYPE_INT16 */ \ + case ATTR_TYPE_INT: /* = ATTR_TYPE_INT32 */ \ case ATTR_TYPE_INT64: \ - case ATTR_TYPE_BYTE: \ + case ATTR_TYPE_UINT8: \ case ATTR_TYPE_UINT16: \ + case ATTR_TYPE_UINT32: \ + case ATTR_TYPE_UINT64: \ case ATTR_TYPE_FLOAT: \ case ATTR_TYPE_DOUBLE: \ case ATTR_TYPE_BOOLEAN: \ @@ -608,31 +667,67 @@ attr_container_get_attr(const attr_container_t *attr_cont, const char *key) short attr_container_get_as_short(const attr_container_t *attr_cont, const char *key) { - TEMPLATE_ATTR_BUF_TO_VALUE(attr_cont, key, s); + TEMPLATE_ATTR_BUF_TO_VALUE(attr_cont, key, i16); +} + +int16_t +attr_container_get_as_int16(const attr_container_t *attr_cont, const char *key) +{ + return (int16_t)attr_container_get_as_short(attr_cont, key); } int attr_container_get_as_int(const attr_container_t *attr_cont, const char *key) { - TEMPLATE_ATTR_BUF_TO_VALUE(attr_cont, key, i); + TEMPLATE_ATTR_BUF_TO_VALUE(attr_cont, key, i32); +} + +int32_t +attr_container_get_as_int32(const attr_container_t *attr_cont, const char *key) +{ + return (int32_t)attr_container_get_as_int(attr_cont, key); +} + +uint32_t +attr_container_get_as_uint32(const attr_container_t *attr_cont, const char *key) +{ + return (uint32_t)attr_container_get_as_int(attr_cont, key); } int64_t attr_container_get_as_int64(const attr_container_t *attr_cont, const char *key) { - TEMPLATE_ATTR_BUF_TO_VALUE(attr_cont, key, j); + TEMPLATE_ATTR_BUF_TO_VALUE(attr_cont, key, i64); +} + +uint64_t +attr_container_get_as_uint64(const attr_container_t *attr_cont, const char *key) +{ + return (uint64_t)attr_container_get_as_int64(attr_cont, key); } int8_t attr_container_get_as_byte(const attr_container_t *attr_cont, const char *key) { - TEMPLATE_ATTR_BUF_TO_VALUE(attr_cont, key, b); + TEMPLATE_ATTR_BUF_TO_VALUE(attr_cont, key, i8); +} + +int8_t +attr_container_get_as_int8(const attr_container_t *attr_cont, const char *key) +{ + return attr_container_get_as_byte(attr_cont, key); +} + +uint8_t +attr_container_get_as_uint8(const attr_container_t *attr_cont, const char *key) +{ + return (uint8_t)attr_container_get_as_byte(attr_cont, key); } uint16_t attr_container_get_as_uint16(const attr_container_t *attr_cont, const char *key) { - TEMPLATE_ATTR_BUF_TO_VALUE(attr_cont, key, s); + return (uint16_t)attr_container_get_as_short(attr_cont, key); } float @@ -671,11 +766,14 @@ attr_container_get_as_bytearray(const attr_container_t *attr_cont, type = *(uint8_t *)addr++; switch (type) { - case ATTR_TYPE_SHORT: - case ATTR_TYPE_INT: + case ATTR_TYPE_BYTE: /* = ATTR_TYPE_INT8 */ + case ATTR_TYPE_SHORT: /* = ATTR_TYPE_INT16 */ + case ATTR_TYPE_INT: /* = ATTR_TYPE_INT32 */ case ATTR_TYPE_INT64: - case ATTR_TYPE_BYTE: + case ATTR_TYPE_UINT8: case ATTR_TYPE_UINT16: + case ATTR_TYPE_UINT32: + case ATTR_TYPE_UINT64: case ATTR_TYPE_FLOAT: case ATTR_TYPE_DOUBLE: case ATTR_TYPE_BOOLEAN: @@ -807,34 +905,52 @@ attr_container_dump(const attr_container_t *attr_cont) attr_container_printf(" key: %s", key); switch (type) { - case ATTR_TYPE_SHORT: - bh_memcpy_s(&value.s, sizeof(int16_t), p, sizeof(int16_t)); + case ATTR_TYPE_BYTE: /* = ATTR_TYPE_INT8 */ + bh_memcpy_s(&value.i8, 1, p, 1); + attr_container_printf(", type: byte, value: 0x%x\n", + value.i8 & 0xFF); + p++; + break; + case ATTR_TYPE_SHORT: /* = ATTR_TYPE_INT16 */ + bh_memcpy_s(&value.i16, sizeof(int16_t), p, sizeof(int16_t)); attr_container_printf(", type: short, value: 0x%x\n", - value.s & 0xFFFF); + value.i16 & 0xFFFF); p += 2; break; - case ATTR_TYPE_INT: - bh_memcpy_s(&value.i, sizeof(int32_t), p, sizeof(int32_t)); - attr_container_printf(", type: int, value: 0x%x\n", value.i); + case ATTR_TYPE_INT: /* = ATTR_TYPE_INT32 */ + bh_memcpy_s(&value.i32, sizeof(int32_t), p, sizeof(int32_t)); + attr_container_printf(", type: int, value: 0x%x\n", value.i32); p += 4; break; case ATTR_TYPE_INT64: - bh_memcpy_s(&value.j, sizeof(uint64_t), p, sizeof(uint64_t)); + bh_memcpy_s(&value.i64, sizeof(int64_t), p, sizeof(int64_t)); attr_container_printf(", type: int64, value: 0x%llx\n", - (long long unsigned int)(value.j)); + (long long unsigned int)(value.i64)); p += 8; break; - case ATTR_TYPE_BYTE: - bh_memcpy_s(&value.b, 1, p, 1); - attr_container_printf(", type: byte, value: 0x%x\n", - value.b & 0xFF); + case ATTR_TYPE_UINT8: + bh_memcpy_s(&value.u8, 1, p, 1); + attr_container_printf(", type: uint8, value: 0x%x\n", value.u8); p++; break; case ATTR_TYPE_UINT16: - bh_memcpy_s(&value.c, sizeof(uint16_t), p, sizeof(uint16_t)); - attr_container_printf(", type: uint16, value: 0x%x\n", value.c); + bh_memcpy_s(&value.u16, sizeof(uint16_t), p, sizeof(uint16_t)); + attr_container_printf(", type: uint16, value: 0x%x\n", + value.u16); p += 2; break; + case ATTR_TYPE_UINT32: + bh_memcpy_s(&value.u32, sizeof(uint32_t), p, sizeof(uint32_t)); + attr_container_printf(", type: uint32, value: 0x%x\n", + value.u32); + p += 4; + break; + case ATTR_TYPE_UINT64: + bh_memcpy_s(&value.u64, sizeof(uint64_t), p, sizeof(uint64_t)); + attr_container_printf(", type: int64, value: 0x%llx\n", + (long long unsigned int)(value.u64)); + p += 8; + break; case ATTR_TYPE_FLOAT: bh_memcpy_s(&value.f, sizeof(float), p, sizeof(float)); attr_container_printf(", type: float, value: %f\n", value.f); diff --git a/core/app-framework/app-native-shared/bi-inc/attr_container.h b/core/app-framework/app-native-shared/bi-inc/attr_container.h index 3b7be8062..f5d8759b8 100644 --- a/core/app-framework/app-native-shared/bi-inc/attr_container.h +++ b/core/app-framework/app-native-shared/bi-inc/attr_container.h @@ -20,13 +20,27 @@ extern "C" { /* Attribute type */ enum { - ATTR_TYPE_BEGIN = 1, - ATTR_TYPE_SHORT = ATTR_TYPE_BEGIN, + ATTR_TYPE_BEGIN = 0, + ATTR_TYPE_BYTE = ATTR_TYPE_BEGIN, + ATTR_TYPE_INT8 = ATTR_TYPE_BYTE, + ATTR_TYPE_SHORT, + ATTR_TYPE_INT16 = ATTR_TYPE_SHORT, ATTR_TYPE_INT, + ATTR_TYPE_INT32 = ATTR_TYPE_INT, ATTR_TYPE_INT64, - ATTR_TYPE_BYTE, + ATTR_TYPE_UINT8, ATTR_TYPE_UINT16, - ATTR_TYPE_FLOAT, + ATTR_TYPE_UINT32, + ATTR_TYPE_UINT64, + /** + * Why ATTR_TYPE_FLOAT = 10? + * We determine the number of bytes that should be copied through 1<<(type & + * 3). ATTR_TYPE_BYTE = 0, so the number of bytes is 1 << 0 = 1. + * ATTR_TYPE_UINT64 = 7, so the number of bytes is 1 << 3 = 8. + * Since the float type takes up 4 bytes, ATTR_TYPE_FLOAT should be 10. + * Calculation: (1 << (10&3)) = (1 << 2) = 4 + */ + ATTR_TYPE_FLOAT = 10, ATTR_TYPE_DOUBLE, ATTR_TYPE_BOOLEAN, ATTR_TYPE_STRING, @@ -89,6 +103,20 @@ bool attr_container_set_short(attr_container_t **p_attr_cont, const char *key, short value); +/** + * Set int16 attribute in attribute container + * + * @param p_attr_cont pointer to attribute container to set attribute, and + * return the new attribute container if it is re-created + * @param key the attribute key + * @param value the attribute value + * + * @return true if success, false otherwise + */ +bool +attr_container_set_int16(attr_container_t **p_attr_cont, const char *key, + int16_t value); + /** * Set int attribute in attribute container * @@ -103,6 +131,34 @@ bool attr_container_set_int(attr_container_t **p_attr_cont, const char *key, int value); +/** + * Set int32 attribute in attribute container + * + * @param p_attr_cont pointer to attribute container to set attribute, and + * return the new attribute container if it is re-created + * @param key the attribute key + * @param value the attribute value + * + * @return true if success, false otherwise + */ +bool +attr_container_set_int32(attr_container_t **p_attr_cont, const char *key, + int32_t value); + +/** + * Set uint32 attribute in attribute container + * + * @param p_attr_cont pointer to attribute container to set attribute, and + * return the new attribute container if it is re-created + * @param key the attribute key + * @param value the attribute value + * + * @return true if success, false otherwise + */ +bool +attr_container_set_uint32(attr_container_t **p_attr_cont, const char *key, + uint32_t value); + /** * Set int64 attribute in attribute container * @@ -117,6 +173,20 @@ bool attr_container_set_int64(attr_container_t **p_attr_cont, const char *key, int64_t value); +/** + * Set uint64 attribute in attribute container + * + * @param p_attr_cont pointer to attribute container to set attribute, and + * return the new attribute container if it is re-created + * @param key the attribute key + * @param value the attribute value + * + * @return true if success, false otherwise + */ +bool +attr_container_set_uint64(attr_container_t **p_attr_cont, const char *key, + uint64_t value); + /** * Set byte attribute in attribute container * @@ -131,6 +201,34 @@ bool attr_container_set_byte(attr_container_t **p_attr_cont, const char *key, int8_t value); +/** + * Set int8 attribute in attribute container + * + * @param p_attr_cont pointer to attribute container to set attribute, and + * return the new attribute container if it is re-created + * @param key the attribute key + * @param value the attribute value + * + * @return true if success, false otherwise + */ +bool +attr_container_set_int8(attr_container_t **p_attr_cont, const char *key, + int8_t value); + +/** + * Set uint8 attribute in attribute container + * + * @param p_attr_cont pointer to attribute container to set attribute, and + * return the new attribute container if it is re-created + * @param key the attribute key + * @param value the attribute value + * + * @return true if success, false otherwise + */ +bool +attr_container_set_uint8(attr_container_t **p_attr_cont, const char *key, + uint8_t value); + /** * Set uint16 attribute in attribute container * @@ -259,6 +357,18 @@ attr_container_contain_key(const attr_container_t *attr_cont, const char *key); short attr_container_get_as_short(const attr_container_t *attr_cont, const char *key); +/** + * Get attribute from attribute container and return it as int16 value, + * return 0 if attribute isn't found in message. + * + * @param attr_cont the attribute container + * @param key the attribute key + * + * @return the short value of the attribute, 0 if key isn't found + */ +int16_t +attr_container_get_as_int16(const attr_container_t *attr_cont, const char *key); + /** * Get attribute from attribute container and return it as int value, * return 0 if attribute isn't found in message. @@ -271,6 +381,31 @@ attr_container_get_as_short(const attr_container_t *attr_cont, const char *key); int attr_container_get_as_int(const attr_container_t *attr_cont, const char *key); +/** + * Get attribute from attribute container and return it as int32 value, + * return 0 if attribute isn't found in message. + * + * @param attr_cont the attribute container + * @param key the attribute key + * + * @return the int value of the attribute, 0 if key isn't found + */ +int32_t +attr_container_get_as_int32(const attr_container_t *attr_cont, const char *key); + +/** + * Get attribute from attribute container and return it as uint32 value, + * return 0 if attribute isn't found in message. + * + * @param attr_cont the attribute container + * @param key the attribute key + * + * @return the unsigned int value of the attribute, 0 if key isn't found + */ +uint32_t +attr_container_get_as_uint32(const attr_container_t *attr_cont, + const char *key); + /** * Get attribute from attribute container and return it as int64 value, * return 0 if attribute isn't found in attribute container. @@ -283,6 +418,19 @@ attr_container_get_as_int(const attr_container_t *attr_cont, const char *key); int64_t attr_container_get_as_int64(const attr_container_t *attr_cont, const char *key); +/** + * Get attribute from attribute container and return it as uint64 value, + * return 0 if attribute isn't found in attribute container. + * + * @param attr_cont the attribute container + * @param key the attribute key + * + * @return the unsigned long value of the attribute, 0 if key isn't found + */ +uint64_t +attr_container_get_as_uint64(const attr_container_t *attr_cont, + const char *key); + /** * Get attribute from attribute container and return it as byte value, * return 0 if attribute isn't found in attribute container. @@ -295,6 +443,30 @@ attr_container_get_as_int64(const attr_container_t *attr_cont, const char *key); int8_t attr_container_get_as_byte(const attr_container_t *attr_cont, const char *key); +/** + * Get attribute from attribute container and return it as int8 value, + * return 0 if attribute isn't found in attribute container. + * + * @param attr_cont the attribute container + * @param key the attribute key + * + * @return the byte value of the attribute, 0 if key isn't found + */ +int8_t +attr_container_get_as_int8(const attr_container_t *attr_cont, const char *key); + +/** + * Get attribute from attribute container and return it as uint8 value, + * return 0 if attribute isn't found in attribute container. + * + * @param attr_cont the attribute container + * @param key the attribute key + * + * @return the uint8 value of the attribute, 0 if key isn't found + */ +uint8_t +attr_container_get_as_uint8(const attr_container_t *attr_cont, const char *key); + /** * Get attribute from attribute container and return it as uint16 value, * return 0 if attribute isn't found in attribute container. diff --git a/core/app-mgr/app-manager/module_wasm_app.c b/core/app-mgr/app-manager/module_wasm_app.c index 7b826d6d9..2005ad8e8 100644 --- a/core/app-mgr/app-manager/module_wasm_app.c +++ b/core/app-mgr/app-manager/module_wasm_app.c @@ -993,6 +993,14 @@ wasm_app_module_uninstall(request_t *msg) app_manager_printf("Uninstall WASM app successful!\n"); +#ifdef COLLECT_CODE_COVERAGE + /* Exit app manager so as to collect code coverage data */ + if (!strcmp(m_name, "__exit_app_manager__")) { + app_manager_printf("Exit app manager\n"); + bh_queue_exit_loop_run(get_app_manager_queue()); + } +#endif + #if VALGRIND_CHECK != 0 bh_queue_exit_loop_run(get_app_manager_queue()); #endif diff --git a/core/config.h b/core/config.h index a7e0ba0a9..a1db3d6bf 100644 --- a/core/config.h +++ b/core/config.h @@ -161,6 +161,17 @@ #define WASM_ENABLE_LIB_PTHREAD_SEMAPHORE 0 #endif +#ifndef WASM_ENABLE_LIB_WASI_THREADS +#define WASM_ENABLE_LIB_WASI_THREADS 0 +#endif + +#ifndef WASM_ENABLE_HEAP_AUX_STACK_ALLOCATION +#define WASM_ENABLE_HEAP_AUX_STACK_ALLOCATION WASM_ENABLE_LIB_WASI_THREADS +#elif WASM_ENABLE_HEAP_AUX_STACK_ALLOCATION == 0 \ + && WASM_ENABLE_LIB_WASI_THREADS == 1 +#error "Heap aux stack allocation must be enabled for WASI threads" +#endif + #ifndef WASM_ENABLE_BASE_LIB #define WASM_ENABLE_BASE_LIB 0 #endif diff --git a/core/iwasm/aot/aot_loader.c b/core/iwasm/aot/aot_loader.c index a15593b90..5345fb2d7 100644 --- a/core/iwasm/aot/aot_loader.c +++ b/core/iwasm/aot/aot_loader.c @@ -1226,7 +1226,10 @@ load_import_globals(const uint8 **p_buf, const uint8 *buf_end, } import_globals[i].global_data_linked = tmp_global.global_data_linked; + import_globals[i].is_linked = true; } +#else + import_globals[i].is_linked = false; #endif import_globals[i].size = wasm_value_type_size(import_globals[i].type); diff --git a/core/iwasm/aot/aot_runtime.c b/core/iwasm/aot/aot_runtime.c index 133c5e38a..ca2ad4f7c 100644 --- a/core/iwasm/aot/aot_runtime.c +++ b/core/iwasm/aot/aot_runtime.c @@ -30,6 +30,8 @@ bh_static_assert(offsetof(WASMExecEnv, aux_stack_boundary) bh_static_assert(offsetof(WASMExecEnv, aux_stack_bottom) == 7 * sizeof(uintptr_t)); bh_static_assert(offsetof(WASMExecEnv, native_symbol) == 8 * sizeof(uintptr_t)); +bh_static_assert(offsetof(WASMExecEnv, native_stack_top_min) + == 9 * sizeof(uintptr_t)); bh_static_assert(offsetof(AOTModuleInstance, memories) == 1 * sizeof(uint64)); bh_static_assert(offsetof(AOTModuleInstance, func_ptrs) == 5 * sizeof(uint64)); @@ -899,83 +901,169 @@ create_exports(AOTModuleInstance *module_inst, AOTModule *module, return create_export_funcs(module_inst, module, error_buf, error_buf_size); } -static bool -clear_wasi_proc_exit_exception(AOTModuleInstance *module_inst) +static AOTFunctionInstance * +lookup_post_instantiate_func(AOTModuleInstance *module_inst, + const char *func_name) { -#if WASM_ENABLE_LIBC_WASI != 0 - const char *exception = aot_get_exception(module_inst); - if (exception && !strcmp(exception, "Exception: wasi proc exit")) { - /* The "wasi proc exit" exception is thrown by native lib to - let wasm app exit, which is a normal behavior, we clear - the exception here. */ - aot_set_exception(module_inst, NULL); - return true; - } - return false; -#else - return false; -#endif -} + AOTFunctionInstance *func; + AOTFuncType *func_type; -static bool -execute_post_inst_function(AOTModuleInstance *module_inst) -{ - AOTFunctionInstance *post_inst_func = - aot_lookup_function(module_inst, "__post_instantiate", "()"); - - if (!post_inst_func) + if (!(func = aot_lookup_function(module_inst, func_name, NULL))) /* Not found */ - return true; + return NULL; - return aot_create_exec_env_and_call_function(module_inst, post_inst_func, 0, - NULL); + func_type = func->u.func.func_type; + if (!(func_type->param_count == 0 && func_type->result_count == 0)) + /* Not a valid function type, ignore it */ + return NULL; + + return func; } static bool -execute_start_function(AOTModuleInstance *module_inst) +execute_post_instantiate_functions(AOTModuleInstance *module_inst, + bool is_sub_inst) { AOTModule *module = (AOTModule *)module_inst->module; - WASMExecEnv *exec_env; - typedef void (*F)(WASMExecEnv *); - union { - F f; - void *v; - } u; + AOTFunctionInstance *initialize_func = NULL; + AOTFunctionInstance *post_inst_func = NULL; + AOTFunctionInstance *call_ctors_func = NULL; +#ifdef OS_ENABLE_HW_BOUND_CHECK + WASMModuleInstanceCommon *module_inst_main = NULL; + WASMExecEnv *exec_env_tls = NULL; +#endif + WASMExecEnv *exec_env = NULL; + bool ret = false; - if (!module->start_function) +#if WASM_ENABLE_LIBC_WASI != 0 + /* + * WASI reactor instances may assume that _initialize will be called by + * the environment at most once, and that none of their other exports + * are accessed before that call. + */ + if (!is_sub_inst && module->import_wasi_api) { + initialize_func = + lookup_post_instantiate_func(module_inst, "_initialize"); + } +#endif + + /* Execute possible "__post_instantiate" function if wasm app is + compiled by emsdk's early version */ + if (!is_sub_inst) { + post_inst_func = + lookup_post_instantiate_func(module_inst, "__post_instantiate"); + } + +#if WASM_ENABLE_BULK_MEMORY != 0 + /* Only execute the memory init function for main instance since + the data segments will be dropped once initialized */ + if (!is_sub_inst +#if WASM_ENABLE_LIBC_WASI != 0 + && !module->import_wasi_api +#endif + ) { + call_ctors_func = + lookup_post_instantiate_func(module_inst, "__wasm_call_ctors"); + } +#endif + + if (!module->start_function && !initialize_func && !post_inst_func + && !call_ctors_func) { + /* No post instantiation functions to call */ return true; + } - if (!(exec_env = - wasm_exec_env_create((WASMModuleInstanceCommon *)module_inst, - module_inst->default_wasm_stack_size))) { +#ifdef OS_ENABLE_HW_BOUND_CHECK + if (is_sub_inst) { + exec_env = exec_env_tls = wasm_runtime_get_exec_env_tls(); + if (exec_env_tls) { + /* Temporarily replace exec_env_tls's module inst to current + module inst to avoid checking failure when calling the + wasm functions, and ensure that the exec_env's module inst + is the correct one. */ + module_inst_main = exec_env_tls->module_inst; + exec_env_tls->module_inst = (WASMModuleInstanceCommon *)module_inst; + } + } +#endif + if (!exec_env + && !(exec_env = + wasm_exec_env_create((WASMModuleInstanceCommon *)module_inst, + module_inst->default_wasm_stack_size))) { aot_set_exception(module_inst, "allocate memory failed"); return false; } - u.v = module->start_function; - u.f(exec_env); + /* Execute start function for both main insance and sub instance */ + if (module->start_function) { + AOTFunctionInstance start_func = { 0 }; + uint32 func_type_idx; + start_func.func_name = ""; + start_func.func_index = module->start_func_index; + start_func.is_import_func = false; + func_type_idx = module->func_type_indexes[module->start_func_index + - module->import_func_count]; + start_func.u.func.func_type = module->func_types[func_type_idx]; + start_func.u.func.func_ptr = module->start_function; + if (!aot_call_function(exec_env, &start_func, 0, NULL)) { + goto fail; + } + } + + if (initialize_func + && !aot_call_function(exec_env, initialize_func, 0, NULL)) { + goto fail; + } + + if (post_inst_func + && !aot_call_function(exec_env, post_inst_func, 0, NULL)) { + goto fail; + } + + if (call_ctors_func + && !aot_call_function(exec_env, call_ctors_func, 0, NULL)) { + goto fail; + } + + ret = true; + +fail: +#ifdef OS_ENABLE_HW_BOUND_CHECK + if (is_sub_inst && exec_env_tls) { + bh_assert(exec_env == exec_env_tls); + /* Restore the exec_env_tls's module inst */ + exec_env_tls->module_inst = module_inst_main; + } + else + wasm_exec_env_destroy(exec_env); +#else wasm_exec_env_destroy(exec_env); - (void)clear_wasi_proc_exit_exception(module_inst); - return !aot_get_exception(module_inst); -} - -#if WASM_ENABLE_BULK_MEMORY != 0 -static bool -execute_memory_init_function(AOTModuleInstance *module_inst) -{ - AOTFunctionInstance *memory_init_func = - aot_lookup_function(module_inst, "__wasm_call_ctors", "()"); - - if (!memory_init_func) - /* Not found */ - return true; - - return aot_create_exec_env_and_call_function(module_inst, memory_init_func, - 0, NULL); -} #endif + return ret; +} + +static bool +check_linked_symbol(AOTModule *module, char *error_buf, uint32 error_buf_size) +{ + uint32 i; + + /* init_func_ptrs() will go through import functions */ + + for (i = 0; i < module->import_global_count; i++) { + AOTImportGlobal *global = module->import_globals + i; + if (!global->is_linked) { + set_error_buf_v(error_buf, error_buf_size, + "failed to link import global (%s, %s)", + global->module_name, global->global_name); + return false; + } + } + + return true; +} + AOTModuleInstance * aot_instantiate(AOTModule *module, bool is_sub_inst, uint32 stack_size, uint32 heap_size, char *error_buf, uint32 error_buf_size) @@ -1059,6 +1147,9 @@ aot_instantiate(AOTModule *module, bool is_sub_inst, uint32 stack_size, if (!init_func_type_indexes(module_inst, module, error_buf, error_buf_size)) goto fail; + if (!check_linked_symbol(module, error_buf, error_buf_size)) + goto fail; + if (!create_exports(module_inst, module, error_buf, error_buf_size)) goto fail; @@ -1079,6 +1170,17 @@ aot_instantiate(AOTModule *module, bool is_sub_inst, uint32 stack_size, } #endif +#if WASM_ENABLE_WASI_NN != 0 + if (!is_sub_inst) { + if (!(((AOTModuleInstanceExtra *)module_inst->e)->wasi_nn_ctx = + wasi_nn_initialize())) { + set_error_buf(error_buf, error_buf_size, + "wasi nn initialization failed"); + goto fail; + } + } +#endif + /* Initialize the thread related data */ if (stack_size == 0) stack_size = DEFAULT_WASM_STACK_SIZE; @@ -1104,32 +1206,11 @@ aot_instantiate(AOTModule *module, bool is_sub_inst, uint32 stack_size, } #endif - /* Execute __post_instantiate function and start function*/ - if (!execute_post_inst_function(module_inst) - || !execute_start_function(module_inst)) { + if (!execute_post_instantiate_functions(module_inst, is_sub_inst)) { set_error_buf(error_buf, error_buf_size, module_inst->cur_exception); goto fail; } -#if WASM_ENABLE_BULK_MEMORY != 0 -#if WASM_ENABLE_LIBC_WASI != 0 - if (!module->import_wasi_api) { -#endif - /* Only execute the memory init function for main instance because - the data segments will be dropped once initialized. - */ - if (!is_sub_inst) { - if (!execute_memory_init_function(module_inst)) { - set_error_buf(error_buf, error_buf_size, - module_inst->cur_exception); - goto fail; - } - } -#if WASM_ENABLE_LIBC_WASI != 0 - } -#endif -#endif - #if WASM_ENABLE_MEMORY_TRACING != 0 wasm_runtime_dump_module_inst_mem_consumption( (WASMModuleInstanceCommon *)module_inst); @@ -1145,6 +1226,15 @@ fail: void aot_deinstantiate(AOTModuleInstance *module_inst, bool is_sub_inst) { + if (module_inst->exec_env_singleton) { + /* wasm_exec_env_destroy will call + wasm_cluster_wait_for_all_except_self to wait for other + threads, so as to destroy their exec_envs and module + instances first, and avoid accessing the shared resources + of current module instance after it is deinstantiated. */ + wasm_exec_env_destroy((WASMExecEnv *)module_inst->exec_env_singleton); + } + #if WASM_ENABLE_LIBC_WASI != 0 /* Destroy wasi resource before freeing app heap, since some fields of wasi contex are allocated from app heap, and if app heap is freed, @@ -1183,13 +1273,19 @@ aot_deinstantiate(AOTModuleInstance *module_inst, bool is_sub_inst) if (module_inst->func_type_indexes) wasm_runtime_free(module_inst->func_type_indexes); - if (module_inst->exec_env_singleton) - wasm_exec_env_destroy((WASMExecEnv *)module_inst->exec_env_singleton); - if (((AOTModuleInstanceExtra *)module_inst->e)->c_api_func_imports) wasm_runtime_free( ((AOTModuleInstanceExtra *)module_inst->e)->c_api_func_imports); +#if WASM_ENABLE_WASI_NN != 0 + if (!is_sub_inst) { + WASINNContext *wasi_nn_ctx = + ((AOTModuleInstanceExtra *)module_inst->e)->wasi_nn_ctx; + if (wasi_nn_ctx) + wasi_nn_destroy(wasi_nn_ctx); + } +#endif + wasm_runtime_free(module_inst); } @@ -1233,6 +1329,7 @@ invoke_native_with_hw_bound_check(WASMExecEnv *exec_env, void *func_ptr, /* Check native stack overflow firstly to ensure we have enough native stack to run the following codes before actually calling the aot function in invokeNative function. */ + RECORD_STACK_USAGE(exec_env, (uint8 *)&module_inst); if ((uint8 *)&module_inst < exec_env->native_stack_boundary + page_size * (guard_page_count + 1)) { aot_set_exception_with_id(module_inst, EXCE_NATIVE_STACK_OVERFLOW); @@ -1384,13 +1481,6 @@ aot_call_function(WASMExecEnv *exec_env, AOTFunctionInstance *function, ret = invoke_native_internal(exec_env, function->u.func.func_ptr, func_type, NULL, NULL, argv1, argc, argv); - if (!ret || aot_get_exception(module_inst)) { - if (clear_wasi_proc_exit_exception(module_inst)) - ret = true; - else - ret = false; - } - #if WASM_ENABLE_DUMP_CALL_STACK != 0 if (!ret) { if (aot_create_call_stack(exec_env)) { @@ -1450,9 +1540,6 @@ aot_call_function(WASMExecEnv *exec_env, AOTFunctionInstance *function, ret = invoke_native_internal(exec_env, function->u.func.func_ptr, func_type, NULL, NULL, argv, argc, argv); - if (clear_wasi_proc_exit_exception(module_inst)) - ret = true; - #if WASM_ENABLE_DUMP_CALL_STACK != 0 if (aot_get_exception(module_inst)) { if (aot_create_call_stack(exec_env)) { @@ -1493,7 +1580,7 @@ aot_create_exec_env_and_call_function(AOTModuleInstance *module_inst, } } - ret = aot_call_function(exec_env, func, argc, argv); + ret = wasm_runtime_call_wasm(exec_env, func, argc, argv); /* don't destroy the exec_env if it isn't created in this function */ if (!existing_exec_env) @@ -1764,7 +1851,9 @@ aot_invoke_native(WASMExecEnv *exec_env, uint32 func_idx, uint32 argc, AOTModuleInstanceExtra *module_inst_extra = (AOTModuleInstanceExtra *)module_inst->e; CApiFuncImport *c_api_func_import = - module_inst_extra->c_api_func_imports + func_idx; + module_inst_extra->c_api_func_imports + ? module_inst_extra->c_api_func_imports + func_idx + : NULL; uint32 *func_type_indexes = module_inst->func_type_indexes; uint32 func_type_idx = func_type_indexes[func_idx]; AOTFuncType *func_type = aot_module->func_types[func_type_idx]; @@ -1780,7 +1869,8 @@ aot_invoke_native(WASMExecEnv *exec_env, uint32 func_idx, uint32 argc, import_func = aot_module->import_funcs + func_idx; if (import_func->call_conv_wasm_c_api) - func_ptr = c_api_func_import->func_ptr_linked; + func_ptr = + c_api_func_import ? c_api_func_import->func_ptr_linked : NULL; if (!func_ptr) { snprintf(buf, sizeof(buf), @@ -1839,6 +1929,7 @@ aot_call_indirect(WASMExecEnv *exec_env, uint32 tbl_idx, uint32 table_elem_idx, exec_env->native_stack_boundary must have been set, we don't set it again */ + RECORD_STACK_USAGE(exec_env, (uint8 *)&module_inst); if ((uint8 *)&module_inst < exec_env->native_stack_boundary) { aot_set_exception_with_id(module_inst, EXCE_NATIVE_STACK_OVERFLOW); goto fail; @@ -1980,9 +2071,6 @@ aot_call_indirect(WASMExecEnv *exec_env, uint32 tbl_idx, uint32 table_elem_idx, } fail: - if (clear_wasi_proc_exit_exception(module_inst)) - return true; - #ifdef OS_ENABLE_HW_BOUND_CHECK wasm_runtime_access_exce_check_guard_page(); #endif diff --git a/core/iwasm/aot/aot_runtime.h b/core/iwasm/aot/aot_runtime.h index a63717299..de12bdaa5 100644 --- a/core/iwasm/aot/aot_runtime.h +++ b/core/iwasm/aot/aot_runtime.h @@ -11,6 +11,10 @@ #include "../interpreter/wasm_runtime.h" #include "../compilation/aot.h" +#if WASM_ENABLE_WASI_NN != 0 +#include "../libraries/wasi-nn/src/wasi_nn_private.h" +#endif + #ifdef __cplusplus extern "C" { #endif @@ -75,6 +79,9 @@ typedef struct AOTFunctionInstance { typedef struct AOTModuleInstanceExtra { CApiFuncImport *c_api_func_imports; +#if WASM_ENABLE_WASI_NN != 0 + WASINNContext *wasi_nn_ctx; +#endif } AOTModuleInstanceExtra; #if defined(OS_ENABLE_HW_BOUND_CHECK) && defined(BH_PLATFORM_WINDOWS) diff --git a/core/iwasm/common/arch/invokeNative_ia32.s b/core/iwasm/common/arch/invokeNative_ia32.s index 0056a53e3..de1c1a5e1 100644 --- a/core/iwasm/common/arch/invokeNative_ia32.s +++ b/core/iwasm/common/arch/invokeNative_ia32.s @@ -16,9 +16,14 @@ _invokeNative: push %ebp movl %esp, %ebp movl 16(%ebp), %ecx /* ecx = argc */ - movl 12(%ebp), %edx /* edx = argv */ + leal 2(%ecx), %edx /* edx = ecx + 2 (count return address and saved ebp) */ + andl $3, %edx /* edx = edx % 4 */ + jz stack_aligned /* if edx == 0, stack is already 16 bytes aligned */ + leal -16(%esp, %edx, 4), %esp /* esp = esp - 16 + edx * 4 */ +stack_aligned: test %ecx, %ecx jz skip_push_args /* if ecx == 0, skip pushing arguments */ + movl 12(%ebp), %edx /* edx = argv */ leal -4(%edx,%ecx,4), %edx /* edx = edx + ecx * 4 - 4 */ subl %esp, %edx /* edx = edx - esp */ 1: diff --git a/core/iwasm/common/wasm_application.c b/core/iwasm/common/wasm_application.c index 8445652f6..1bb9f29b6 100644 --- a/core/iwasm/common/wasm_application.c +++ b/core/iwasm/common/wasm_application.c @@ -203,22 +203,12 @@ wasm_application_execute_main(WASMModuleInstanceCommon *module_inst, int32 argc, char *argv[]) { bool ret; -#if WASM_ENABLE_THREAD_MGR != 0 - WASMCluster *cluster; -#endif -#if WASM_ENABLE_THREAD_MGR != 0 || WASM_ENABLE_MEMORY_PROFILING != 0 +#if WASM_ENABLE_MEMORY_PROFILING != 0 WASMExecEnv *exec_env; #endif ret = execute_main(module_inst, argc, argv); -#if WASM_ENABLE_THREAD_MGR != 0 - exec_env = wasm_runtime_get_exec_env_singleton(module_inst); - if (exec_env && (cluster = wasm_exec_env_get_cluster(exec_env))) { - wasm_cluster_wait_for_all_except_self(cluster, exec_env); - } -#endif - #if WASM_ENABLE_MEMORY_PROFILING != 0 exec_env = wasm_runtime_get_exec_env_singleton(module_inst); if (exec_env) { @@ -622,22 +612,12 @@ wasm_application_execute_func(WASMModuleInstanceCommon *module_inst, const char *name, int32 argc, char *argv[]) { bool ret; -#if WASM_ENABLE_THREAD_MGR != 0 - WASMCluster *cluster; -#endif -#if WASM_ENABLE_THREAD_MGR != 0 || WASM_ENABLE_MEMORY_PROFILING != 0 +#if WASM_ENABLE_MEMORY_PROFILING != 0 WASMExecEnv *exec_env; #endif ret = execute_func(module_inst, name, argc, argv); -#if WASM_ENABLE_THREAD_MGR != 0 - exec_env = wasm_runtime_get_exec_env_singleton(module_inst); - if (exec_env && (cluster = wasm_exec_env_get_cluster(exec_env))) { - wasm_cluster_wait_for_all_except_self(cluster, exec_env); - } -#endif - #if WASM_ENABLE_MEMORY_PROFILING != 0 exec_env = wasm_runtime_get_exec_env_singleton(module_inst); if (exec_env) { diff --git a/core/iwasm/common/wasm_c_api.c b/core/iwasm/common/wasm_c_api.c index d8b984136..9f6ed1e5d 100644 --- a/core/iwasm/common/wasm_c_api.c +++ b/core/iwasm/common/wasm_c_api.c @@ -276,7 +276,7 @@ WASM_DEFINE_VEC_OWN(store, wasm_store_delete) WASM_DEFINE_VEC_OWN(valtype, wasm_valtype_delete) #ifndef NDEBUG -#if WAMR_BUILD_MEMORY_PROFILING != 0 +#if WASM_ENABLE_MEMORY_PROFILING != 0 #define WASM_C_DUMP_PROC_MEM() LOG_PROC_MEM() #else #define WASM_C_DUMP_PROC_MEM() (void)0 @@ -398,7 +398,7 @@ wasm_engine_new_internal(mem_alloc_type_t type, const MemAllocOption *opts) } /* global engine instance */ -static wasm_engine_t *singleton_engine = NULL; +static wasm_engine_t *singleton_engine; #ifdef os_thread_local_attribute /* categorize wasm_store_t as threads*/ static os_thread_local_attribute unsigned thread_local_stores_num = 0; @@ -1458,6 +1458,30 @@ wasm_importtype_type(const wasm_importtype_t *import_type) return import_type->extern_type; } +bool +wasm_importtype_is_linked(const wasm_importtype_t *import_type) +{ + if (!import_type) + return false; + + const wasm_name_t *module_name = wasm_importtype_module(import_type); + const wasm_name_t *field_name = wasm_importtype_name(import_type); + + switch (wasm_externtype_kind(wasm_importtype_type(import_type))) { + case WASM_EXTERN_FUNC: + return wasm_runtime_is_import_func_linked(module_name->data, + field_name->data); + case WASM_EXTERN_GLOBAL: + return wasm_runtime_is_import_global_linked(module_name->data, + field_name->data); + case WASM_EXTERN_MEMORY: + case WASM_EXTERN_TABLE: + default: + break; + } + return false; +} + own wasm_exporttype_t * wasm_exporttype_new(own wasm_byte_vec_t *name, own wasm_externtype_t *extern_type) @@ -1633,8 +1657,6 @@ wasm_val_to_rt_val(WASMModuleInstanceCommon *inst_comm_rt, uint8 val_type_rt, ret = wasm_externref_obj2ref(inst_comm_rt, v->of.ref, (uint32 *)data); break; -#else - (void)inst_comm_rt; #endif default: LOG_WARNING("unexpected value type %d", val_type_rt); @@ -1642,6 +1664,7 @@ wasm_val_to_rt_val(WASMModuleInstanceCommon *inst_comm_rt, uint8 val_type_rt, break; } + (void)inst_comm_rt; return ret; } @@ -2537,12 +2560,12 @@ wasm_module_imports(const wasm_module_t *module, own wasm_importtype_vec_t *out) bh_assert(extern_type); - wasm_name_new_from_string(&module_name, module_name_rt); + wasm_name_new_from_string_nt(&module_name, module_name_rt); if (strlen(module_name_rt) && !module_name.data) { goto failed; } - wasm_name_new_from_string(&name, field_name_rt); + wasm_name_new_from_string_nt(&name, field_name_rt); if (strlen(field_name_rt) && !name.data) { goto failed; } @@ -2622,7 +2645,7 @@ wasm_module_exports(const wasm_module_t *module, wasm_exporttype_vec_t *out) } /* byte* -> wasm_byte_vec_t */ - wasm_name_new_from_string(&name, export->name); + wasm_name_new_from_string_nt(&name, export->name); if (strlen(export->name) && !name.data) { goto failed; } @@ -3008,6 +3031,20 @@ failed: return NULL; } +static wasm_func_t * +wasm_func_new_empty(wasm_store_t *store) +{ + wasm_func_t *func = NULL; + + if (!(func = malloc_internal(sizeof(wasm_func_t)))) + goto failed; + + func->store = store; + func->kind = WASM_EXTERN_FUNC; + + RETURN_OBJ(func, wasm_func_delete) +} + void wasm_func_delete(wasm_func_t *func) { @@ -3211,7 +3248,8 @@ wasm_func_call(const wasm_func_t *func, const wasm_val_vec_t *params, wasm_name_t message = { 0 }; wasm_trap_t *trap; - wasm_name_new_from_string(&message, "failed to call unlinked function"); + wasm_name_new_from_string_nt(&message, + "failed to call unlinked function"); trap = wasm_trap_new(func->store, &message); wasm_byte_vec_delete(&message); @@ -3371,6 +3409,25 @@ failed: return NULL; } +static wasm_global_t * +wasm_global_new_empty(wasm_store_t *store) +{ + wasm_global_t *global = NULL; + + global = malloc_internal(sizeof(wasm_global_t)); + if (!global) + goto failed; + + global->store = store; + global->kind = WASM_EXTERN_GLOBAL; + + return global; +failed: + LOG_DEBUG("%s failed", __FUNCTION__); + wasm_global_delete(global); + return NULL; +} + /* almost same with wasm_global_new */ wasm_global_t * wasm_global_copy(const wasm_global_t *src) @@ -4205,7 +4262,8 @@ wasm_memory_data_size(const wasm_memory_t *memory) (WASMModuleInstance *)module_inst_comm; WASMMemoryInstance *memory_inst = module_inst->memories[memory->memory_idx_rt]; - return memory_inst->cur_page_count * memory_inst->num_bytes_per_page; + return (size_t)memory_inst->cur_page_count + * memory_inst->num_bytes_per_page; } #endif @@ -4215,7 +4273,8 @@ wasm_memory_data_size(const wasm_memory_t *memory) AOTMemoryInstance *memory_inst = ((AOTMemoryInstance **) module_inst->memories)[memory->memory_idx_rt]; - return memory_inst->cur_page_count * memory_inst->num_bytes_per_page; + return (size_t)memory_inst->cur_page_count + * memory_inst->num_bytes_per_page; } #endif @@ -4286,6 +4345,11 @@ interp_link_func(const wasm_instance_t *inst, const WASMModule *module_interp, imported_func_interp = module_interp->import_functions + func_idx_rt; bh_assert(imported_func_interp); + bh_assert(imported_func_interp->kind == IMPORT_KIND_FUNC); + + /* it is a placeholder and let's skip it*/ + if (!import->type) + return true; /* type comparison */ if (!wasm_functype_same_internal( @@ -4300,6 +4364,8 @@ interp_link_func(const wasm_instance_t *inst, const WASMModule *module_interp, imported_func_interp->u.function.func_ptr_linked = import->u.cb_env.cb; else imported_func_interp->u.function.func_ptr_linked = import->u.cb; + bh_assert(imported_func_interp->u.function.func_ptr_linked); + import->func_idx_rt = func_idx_rt; (void)inst; @@ -4318,12 +4384,19 @@ interp_link_global(const WASMModule *module_interp, uint16 global_idx_rt, imported_global_interp = module_interp->import_globals + global_idx_rt; bh_assert(imported_global_interp); + bh_assert(imported_global_interp->kind == IMPORT_KIND_GLOBAL); + /* it is a placeholder and let's skip it*/ + if (!import->type) + return true; + + /* type comparison */ if (!cmp_val_kind_with_val_type(wasm_valtype_kind(import->type->val_type), imported_global_interp->u.global.type)) return false; /* set init value */ + bh_assert(import->init); switch (wasm_valtype_kind(import->type->val_type)) { case WASM_I32: imported_global_interp->u.global.global_data_linked.i32 = @@ -4350,58 +4423,6 @@ interp_link_global(const WASMModule *module_interp, uint16 global_idx_rt, return true; } -static bool -interp_link(const wasm_instance_t *inst, const WASMModule *module_interp, - wasm_extern_t *imports[]) -{ - uint32 i = 0; - uint32 import_func_i = 0; - uint32 import_global_i = 0; - - bh_assert(inst && module_interp && imports); - - for (i = 0; i < module_interp->import_count; ++i) { - wasm_extern_t *import = imports[i]; - WASMImport *import_rt = module_interp->imports + i; - - switch (import_rt->kind) { - case IMPORT_KIND_FUNC: - { - if (!interp_link_func(inst, module_interp, import_func_i, - wasm_extern_as_func(import))) { - LOG_WARNING("link #%d function failed", import_func_i); - goto failed; - } - import_func_i++; - break; - } - case IMPORT_KIND_GLOBAL: - { - if (!interp_link_global(module_interp, import_global_i, - wasm_extern_as_global(import))) { - LOG_WARNING("link #%d global failed", import_global_i); - goto failed; - } - import_global_i++; - break; - } - case IMPORT_KIND_MEMORY: - case IMPORT_KIND_TABLE: - default: - ASSERT_NOT_IMPLEMENTED(); - LOG_WARNING("%s meets unsupported kind: %d", __FUNCTION__, - import_rt->kind); - goto failed; - } - } - - return true; - -failed: - LOG_DEBUG("%s failed", __FUNCTION__); - return false; -} - static bool interp_process_export(wasm_store_t *store, const WASMModuleInstance *inst_interp, @@ -4501,6 +4522,10 @@ aot_link_func(const wasm_instance_t *inst, const AOTModule *module_aot, import_aot_func = module_aot->import_funcs + import_func_idx_rt; bh_assert(import_aot_func); + /* it is a placeholder and let's skip it*/ + if (!import->type) + return true; + /* type comparison */ if (!wasm_functype_same_internal(import->type, import_aot_func->func_type)) return false; @@ -4513,6 +4538,8 @@ aot_link_func(const wasm_instance_t *inst, const AOTModule *module_aot, import_aot_func->func_ptr_linked = import->u.cb_env.cb; else import_aot_func->func_ptr_linked = import->u.cb; + bh_assert(import_aot_func->func_ptr_linked); + import->func_idx_rt = import_func_idx_rt; return true; @@ -4530,6 +4557,10 @@ aot_link_global(const AOTModule *module_aot, uint16 global_idx_rt, import_aot_global = module_aot->import_globals + global_idx_rt; bh_assert(import_aot_global); + /* it is a placeholder and let's skip it*/ + if (!import->type) + return true; + val_type = wasm_globaltype_content(import->type); bh_assert(val_type); @@ -4537,6 +4568,7 @@ aot_link_global(const AOTModule *module_aot, uint16 global_idx_rt, import_aot_global->type)) return false; + bh_assert(import->init); switch (wasm_valtype_kind(val_type)) { case WASM_I32: import_aot_global->global_data_linked.i32 = import->init->of.i32; @@ -4555,63 +4587,8 @@ aot_link_global(const AOTModule *module_aot, uint16 global_idx_rt, } import->global_idx_rt = global_idx_rt; + import_aot_global->is_linked = true; return true; - -failed: - LOG_DEBUG("%s failed", __FUNCTION__); - return false; -} - -static bool -aot_link(const wasm_instance_t *inst, const AOTModule *module_aot, - wasm_extern_t *imports[]) -{ - uint32 i = 0; - uint32 import_func_i = 0; - uint32 import_global_i = 0; - wasm_extern_t *import = NULL; - wasm_func_t *func = NULL; - wasm_global_t *global = NULL; - - bh_assert(inst && module_aot && imports); - - while (import_func_i < module_aot->import_func_count - || import_global_i < module_aot->import_global_count) { - import = imports[i++]; - - bh_assert(import); - - switch (wasm_extern_kind(import)) { - case WASM_EXTERN_FUNC: - bh_assert(import_func_i < module_aot->import_func_count); - func = wasm_extern_as_func((wasm_extern_t *)import); - if (!aot_link_func(inst, module_aot, import_func_i, func)) { - LOG_WARNING("link #%d function failed", import_func_i); - goto failed; - } - import_func_i++; - - break; - case WASM_EXTERN_GLOBAL: - bh_assert(import_global_i < module_aot->import_global_count); - global = wasm_extern_as_global((wasm_extern_t *)import); - if (!aot_link_global(module_aot, import_global_i, global)) { - LOG_WARNING("link #%d global failed", import_global_i); - goto failed; - } - import_global_i++; - - break; - case WASM_EXTERN_MEMORY: - case WASM_EXTERN_TABLE: - default: - ASSERT_NOT_IMPLEMENTED(); - goto failed; - } - } - - return true; - failed: LOG_DEBUG("%s failed", __FUNCTION__); return false; @@ -4692,7 +4669,7 @@ aot_process_export(wasm_store_t *store, const AOTModuleInstance *inst_aot, goto failed; } - wasm_name_new_from_string(external->name, export->name); + wasm_name_new_from_string_nt(external->name, export->name); if (strlen(export->name) && !external->name->data) { goto failed; } @@ -4710,6 +4687,95 @@ failed: } #endif /* WASM_ENABLE_AOT */ +static bool +do_link(const wasm_instance_t *inst, const wasm_module_t *module, + const wasm_extern_vec_t *imports) +{ + uint32 i, import_func_i, import_global_i; + + bh_assert(inst && module); + + /* we have run a module_type check before. */ + + for (i = 0, import_func_i = 0, import_global_i = 0; i < imports->num_elems; + i++) { + wasm_extern_t *import = imports->data[i]; + + if (!import) { + LOG_ERROR("imports[%d] is NULL and it is fatal\n", i); + goto failed; + } + + switch (wasm_extern_kind(import)) { + case WASM_EXTERN_FUNC: + { + bool ret = false; +#if WASM_ENABLE_INTERP != 0 + if ((*module)->module_type == Wasm_Module_Bytecode) { + ret = interp_link_func(inst, MODULE_INTERP(module), + import_func_i, + wasm_extern_as_func(import)); + } +#endif +#if WASM_ENABLE_AOT != 0 + if ((*module)->module_type == Wasm_Module_AoT) { + ret = aot_link_func(inst, MODULE_AOT(module), import_func_i, + wasm_extern_as_func(import)); + } +#endif + if (!ret) { + LOG_WARNING("link function #%d failed", import_func_i); + goto failed; + } + + import_func_i++; + break; + } + case WASM_EXTERN_GLOBAL: + { + bool ret = false; +#if WASM_ENABLE_INTERP != 0 + if ((*module)->module_type == Wasm_Module_Bytecode) { + ret = interp_link_global(MODULE_INTERP(module), + import_global_i, + wasm_extern_as_global(import)); + } +#endif +#if WASM_ENABLE_AOT != 0 + if ((*module)->module_type == Wasm_Module_AoT) { + ret = aot_link_global(MODULE_AOT(module), import_global_i, + wasm_extern_as_global(import)); + } +#endif + if (!ret) { + LOG_WARNING("link global #%d failed", import_global_i); + goto failed; + } + + import_global_i++; + break; + } + case WASM_EXTERN_MEMORY: + case WASM_EXTERN_TABLE: + { + LOG_WARNING("doesn't support import memories and tables for " + "now, ignore them"); + break; + } + default: + { + UNREACHABLE(); + break; + } + } + } + + return true; +failed: + LOG_DEBUG("%s failed", __FUNCTION__); + return false; +} + wasm_instance_t * wasm_instance_new(wasm_store_t *store, const wasm_module_t *module, const wasm_extern_vec_t *imports, own wasm_trap_t **trap) @@ -4718,57 +4784,6 @@ wasm_instance_new(wasm_store_t *store, const wasm_module_t *module, KILOBYTE(32), KILOBYTE(32)); } -static bool -compare_imports(const wasm_module_t *module, const wasm_extern_vec_t *imports) -{ - unsigned import_func_count = 0; - unsigned import_global_count = 0; - unsigned import_memory_count = 0; - unsigned import_table_count = 0; - unsigned i = 0; - - for (i = 0; imports && i < imports->num_elems; i++) { - wasm_extern_t *import = imports->data[i]; - switch (wasm_extern_kind(import)) { - case WASM_EXTERN_FUNC: - import_func_count++; - break; - case WASM_EXTERN_GLOBAL: - import_global_count++; - break; - case WASM_EXTERN_MEMORY: - import_memory_count++; - break; - case WASM_EXTERN_TABLE: - import_table_count++; - break; - default: - UNREACHABLE(); - return false; - } - } - -#if WASM_ENABLE_INTERP != 0 - if ((*module)->module_type == Wasm_Module_Bytecode) - return import_func_count == MODULE_INTERP(module)->import_function_count - && import_global_count - == MODULE_INTERP(module)->import_global_count - && import_memory_count - == MODULE_INTERP(module)->import_memory_count - && import_table_count - == MODULE_INTERP(module)->import_table_count; -#endif -#if WASM_ENABLE_AOT != 0 - if ((*module)->module_type == Wasm_Module_AoT) - return import_func_count == MODULE_AOT(module)->import_func_count - && import_global_count == MODULE_AOT(module)->import_global_count - && import_memory_count == MODULE_AOT(module)->import_memory_count - && import_table_count == MODULE_AOT(module)->import_table_count; -#endif - - return false; -} - wasm_instance_t * wasm_instance_new_with_args(wasm_store_t *store, const wasm_module_t *module, const wasm_extern_vec_t *imports, @@ -4778,7 +4793,6 @@ wasm_instance_new_with_args(wasm_store_t *store, const wasm_module_t *module, char sub_error_buf[128] = { 0 }; char error_buf[256] = { 0 }; wasm_instance_t *instance = NULL; - WASMModuleInstance *inst_rt; CApiFuncImport *func_import = NULL, **p_func_imports = NULL; uint32 i = 0, import_func_count = 0; uint64 total_size; @@ -4789,11 +4803,9 @@ wasm_instance_new_with_args(wasm_store_t *store, const wasm_module_t *module, if (!module) return NULL; - if (!compare_imports(module, imports)) { - snprintf(sub_error_buf, sizeof(sub_error_buf), - "Failed to match imports"); - goto failed; - } + /* + * will do the check at the end of wasm_runtime_instantiate + */ WASM_C_DUMP_PROC_MEM(); @@ -4804,43 +4816,17 @@ wasm_instance_new_with_args(wasm_store_t *store, const wasm_module_t *module, goto failed; } - /* link module and imports */ - if (imports && imports->num_elems) { - bool link = false; -#if WASM_ENABLE_INTERP != 0 - if ((*module)->module_type == Wasm_Module_Bytecode) { - if (!interp_link(instance, MODULE_INTERP(module), - (wasm_extern_t **)imports->data)) { - snprintf(sub_error_buf, sizeof(sub_error_buf), - "Failed to validate imports"); - goto failed; - } - link = true; - } -#endif - -#if WASM_ENABLE_AOT != 0 - if ((*module)->module_type == Wasm_Module_AoT) { - if (!aot_link(instance, MODULE_AOT(module), - (wasm_extern_t **)imports->data)) { - snprintf(sub_error_buf, sizeof(sub_error_buf), - "Failed to validate imports"); - goto failed; - } - link = true; - } -#endif - - /* - * a wrong combination of module filetype and compilation flags - * also leads to below branch - */ - if (!link) { + /* executes the instantiate-time linking if provided */ + if (imports) { + if (!do_link(instance, module, imports)) { snprintf(sub_error_buf, sizeof(sub_error_buf), - "Failed to verify import count"); + "Failed to validate imports"); goto failed; } } + /* + * will do the linking result check at the end of wasm_runtime_instantiate + */ instance->inst_comm_rt = wasm_runtime_instantiate( *module, stack_size, heap_size, sub_error_buf, sizeof(sub_error_buf)); @@ -4855,18 +4841,22 @@ wasm_instance_new_with_args(wasm_store_t *store, const wasm_module_t *module, } /* create the c-api func import list */ - inst_rt = (WASMModuleInstance *)instance->inst_comm_rt; #if WASM_ENABLE_INTERP != 0 if (instance->inst_comm_rt->module_type == Wasm_Module_Bytecode) { - p_func_imports = &inst_rt->e->c_api_func_imports; - import_func_count = inst_rt->module->import_function_count; + WASMModuleInstanceExtra *e = + ((WASMModuleInstance *)instance->inst_comm_rt)->e; + p_func_imports = &(e->c_api_func_imports); + import_func_count = MODULE_INTERP(module)->import_function_count; } #endif #if WASM_ENABLE_AOT != 0 if (instance->inst_comm_rt->module_type == Wasm_Module_AoT) { - p_func_imports = - &((AOTModuleInstanceExtra *)inst_rt->e)->c_api_func_imports; - import_func_count = ((AOTModule *)inst_rt->module)->import_func_count; + AOTModuleInstanceExtra *e = + (AOTModuleInstanceExtra *)((AOTModuleInstance *) + instance->inst_comm_rt) + ->e; + p_func_imports = &(e->c_api_func_imports); + import_func_count = MODULE_AOT(module)->import_func_count; } #endif bh_assert(p_func_imports); @@ -4879,16 +4869,21 @@ wasm_instance_new_with_args(wasm_store_t *store, const wasm_module_t *module, goto failed; } - /* fill in c-api func import list */ + /* fill in module_inst->e->c_api_func_imports */ for (i = 0; imports && i < imports->num_elems; i++) { - wasm_func_t *func_host; - wasm_extern_t *in; + wasm_func_t *func_host = NULL; + wasm_extern_t *in = imports->data[i]; + bh_assert(in); - in = imports->data[i]; if (wasm_extern_kind(in) != WASM_EXTERN_FUNC) continue; func_host = wasm_extern_as_func(in); + /* it is a placeholder and let's skip it*/ + if (!func_host->type) { + func_import++; + continue; + } func_import->with_env_arg = func_host->with_env; if (func_host->with_env) { @@ -4899,6 +4894,7 @@ wasm_instance_new_with_args(wasm_store_t *store, const wasm_module_t *module, func_import->func_ptr_linked = func_host->u.cb; func_import->env_arg = NULL; } + bh_assert(func_import->func_ptr_linked); func_import++; } @@ -4906,6 +4902,8 @@ wasm_instance_new_with_args(wasm_store_t *store, const wasm_module_t *module, /* fill with inst */ for (i = 0; imports && imports->data && i < imports->num_elems; ++i) { wasm_extern_t *import = imports->data[i]; + bh_assert(import); + switch (import->kind) { case WASM_EXTERN_FUNC: wasm_extern_as_func(import)->inst_comm_rt = @@ -5001,7 +4999,7 @@ failed: sub_error_buf); if (trap != NULL) { wasm_message_t message = { 0 }; - wasm_name_new_from_string(&message, error_buf); + wasm_name_new_from_string_nt(&message, error_buf); *trap = wasm_trap_new(store, &message); wasm_byte_vec_delete(&message); } @@ -5201,3 +5199,16 @@ BASIC_FOUR_LIST(WASM_EXTERN_AS_OTHER_CONST) BASIC_FOUR_LIST(WASM_OTHER_AS_EXTERN_CONST) #undef WASM_OTHER_AS_EXTERN_CONST + +wasm_extern_t * +wasm_extern_new_empty(wasm_store_t *store, wasm_externkind_t extern_kind) +{ + if (extern_kind == WASM_EXTERN_FUNC) + return wasm_func_as_extern(wasm_func_new_empty(store)); + + if (extern_kind == WASM_EXTERN_GLOBAL) + return wasm_global_as_extern(wasm_global_new_empty(store)); + + LOG_ERROR("Don't support linking table and memory for now"); + return NULL; +} diff --git a/core/iwasm/common/wasm_exec_env.c b/core/iwasm/common/wasm_exec_env.c index 515a60b8e..622bcd71e 100644 --- a/core/iwasm/common/wasm_exec_env.c +++ b/core/iwasm/common/wasm_exec_env.c @@ -172,15 +172,18 @@ void wasm_exec_env_destroy(WASMExecEnv *exec_env) { #if WASM_ENABLE_THREAD_MGR != 0 - /* Terminate all sub-threads */ + /* Wait for all sub-threads */ WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env); if (cluster) { - wasm_cluster_terminate_all_except_self(cluster, exec_env); + wasm_cluster_wait_for_all_except_self(cluster, exec_env); #if WASM_ENABLE_DEBUG_INTERP != 0 /* Must fire exit event after other threads exits, otherwise the stopped thread will be overrided by other threads */ wasm_cluster_thread_exited(exec_env); #endif + /* We have waited for other threads, this is the only alive thread, so + * we don't acquire cluster->lock because the cluster will be destroyed + * inside this function */ wasm_cluster_del_exec_env(cluster, exec_env); } #endif /* end of WASM_ENABLE_THREAD_MGR */ @@ -205,9 +208,17 @@ void wasm_exec_env_set_thread_info(WASMExecEnv *exec_env) { uint8 *stack_boundary = os_thread_get_stack_boundary(); + +#if WASM_ENABLE_THREAD_MGR != 0 + os_mutex_lock(&exec_env->wait_lock); +#endif exec_env->handle = os_self_thread(); exec_env->native_stack_boundary = stack_boundary ? stack_boundary + WASM_STACK_GUARD_SIZE : NULL; + exec_env->native_stack_top_min = (void *)UINTPTR_MAX; +#if WASM_ENABLE_THREAD_MGR != 0 + os_mutex_unlock(&exec_env->wait_lock); +#endif } #if WASM_ENABLE_THREAD_MGR != 0 diff --git a/core/iwasm/common/wasm_exec_env.h b/core/iwasm/common/wasm_exec_env.h index 398292079..29b28a159 100644 --- a/core/iwasm/common/wasm_exec_env.h +++ b/core/iwasm/common/wasm_exec_env.h @@ -84,6 +84,12 @@ typedef struct WASMExecEnv { void **native_symbol; #endif + /* + * The lowest stack pointer value observed. + * Assumption: native stack grows to the lower address. + */ + uint8 *native_stack_top_min; + #if WASM_ENABLE_FAST_JIT != 0 /** * Cache for @@ -165,6 +171,17 @@ typedef struct WASMExecEnv { } wasm_stack; } WASMExecEnv; +#if WASM_ENABLE_MEMORY_PROFILING != 0 +#define RECORD_STACK_USAGE(e, p) \ + do { \ + if ((e)->native_stack_top_min > (p)) { \ + (e)->native_stack_top_min = (p); \ + } \ + } while (0) +#else +#define RECORD_STACK_USAGE(e, p) (void)0 +#endif + WASMExecEnv * wasm_exec_env_create_internal(struct WASMModuleInstanceCommon *module_inst, uint32 stack_size); @@ -179,6 +196,13 @@ wasm_exec_env_create(struct WASMModuleInstanceCommon *module_inst, void wasm_exec_env_destroy(WASMExecEnv *exec_env); +static inline bool +wasm_exec_env_is_aux_stack_managed_by_runtime(WASMExecEnv *exec_env) +{ + return exec_env->aux_stack_boundary.boundary != 0 + || exec_env->aux_stack_bottom.bottom != 0; +} + /** * Allocate a WASM frame from the WASM stack. * diff --git a/core/iwasm/common/wasm_memory.c b/core/iwasm/common/wasm_memory.c index 0aa2d6415..82676ae27 100644 --- a/core/iwasm/common/wasm_memory.c +++ b/core/iwasm/common/wasm_memory.c @@ -8,6 +8,10 @@ #include "bh_platform.h" #include "mem_alloc.h" +#if WASM_ENABLE_SHARED_MEMORY != 0 +#include "../common/wasm_shared_memory.h" +#endif + typedef enum Memory_Mode { MEMORY_MODE_UNKNOWN = 0, MEMORY_MODE_POOL, @@ -506,7 +510,7 @@ wasm_get_default_memory(WASMModuleInstance *module_inst) #ifndef OS_ENABLE_HW_BOUND_CHECK bool -wasm_enlarge_memory(WASMModuleInstance *module, uint32 inc_page_count) +wasm_enlarge_memory_internal(WASMModuleInstance *module, uint32 inc_page_count) { WASMMemoryInstance *memory = wasm_get_default_memory(module); uint8 *memory_data_old, *memory_data_new, *heap_data_old; @@ -624,7 +628,7 @@ wasm_enlarge_memory(WASMModuleInstance *module, uint32 inc_page_count) } #else bool -wasm_enlarge_memory(WASMModuleInstance *module, uint32 inc_page_count) +wasm_enlarge_memory_internal(WASMModuleInstance *module, uint32 inc_page_count) { WASMMemoryInstance *memory = wasm_get_default_memory(module); uint32 num_bytes_per_page, total_size_old; @@ -697,3 +701,59 @@ wasm_enlarge_memory(WASMModuleInstance *module, uint32 inc_page_count) return true; } #endif /* end of OS_ENABLE_HW_BOUND_CHECK */ + +bool +wasm_enlarge_memory(WASMModuleInstance *module, uint32 inc_page_count) +{ + bool ret = false; + +#if WASM_ENABLE_SHARED_MEMORY != 0 + WASMSharedMemNode *node = + wasm_module_get_shared_memory((WASMModuleCommon *)module->module); + if (node) + os_mutex_lock(&node->shared_mem_lock); +#endif + ret = wasm_enlarge_memory_internal(module, inc_page_count); +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (node) + os_mutex_unlock(&node->shared_mem_lock); +#endif + + return ret; +} + +#if !defined(OS_ENABLE_HW_BOUND_CHECK) \ + || WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 \ + || WASM_ENABLE_BULK_MEMORY != 0 +uint32 +wasm_get_num_bytes_per_page(WASMMemoryInstance *memory, void *node) +{ + uint32 num_bytes_per_page; +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (node) + os_mutex_lock(&((WASMSharedMemNode *)node)->shared_mem_lock); +#endif + num_bytes_per_page = memory->num_bytes_per_page; +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (node) + os_mutex_unlock(&((WASMSharedMemNode *)node)->shared_mem_lock); +#endif + return num_bytes_per_page; +} + +uint32 +wasm_get_linear_memory_size(WASMMemoryInstance *memory, void *node) +{ + uint32 linear_mem_size; +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (node) + os_mutex_lock(&((WASMSharedMemNode *)node)->shared_mem_lock); +#endif + linear_mem_size = memory->num_bytes_per_page * memory->cur_page_count; +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (node) + os_mutex_unlock(&((WASMSharedMemNode *)node)->shared_mem_lock); +#endif + return linear_mem_size; +} +#endif \ No newline at end of file diff --git a/core/iwasm/common/wasm_memory.h b/core/iwasm/common/wasm_memory.h index b5f3f78c7..1324742fe 100644 --- a/core/iwasm/common/wasm_memory.h +++ b/core/iwasm/common/wasm_memory.h @@ -8,6 +8,7 @@ #include "bh_common.h" #include "../include/wasm_export.h" +#include "../interpreter/wasm_runtime.h" #ifdef __cplusplus extern "C" { @@ -23,6 +24,16 @@ wasm_runtime_memory_destroy(); unsigned wasm_runtime_memory_pool_size(); +#if !defined(OS_ENABLE_HW_BOUND_CHECK) \ + || WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 \ + || WASM_ENABLE_BULK_MEMORY != 0 +uint32 +wasm_get_num_bytes_per_page(WASMMemoryInstance *memory, void *node); + +uint32 +wasm_get_linear_memory_size(WASMMemoryInstance *memory, void *node); +#endif + #ifdef __cplusplus } #endif diff --git a/core/iwasm/common/wasm_native.c b/core/iwasm/common/wasm_native.c index 0fa596277..1acaed6ee 100644 --- a/core/iwasm/common/wasm_native.c +++ b/core/iwasm/common/wasm_native.c @@ -53,6 +53,17 @@ uint32 get_lib_pthread_export_apis(NativeSymbol **p_lib_pthread_apis); #endif +#if WASM_ENABLE_LIB_WASI_THREADS != 0 +bool +lib_wasi_threads_init(void); + +void +lib_wasi_threads_destroy(void); + +uint32 +get_lib_wasi_threads_export_apis(NativeSymbol **p_lib_wasi_threads_apis); +#endif + uint32 get_libc_emcc_export_apis(NativeSymbol **p_libc_emcc_apis); @@ -239,6 +250,10 @@ lookup_symbol(NativeSymbol *native_symbols, uint32 n_native_symbols, return NULL; } +/** + * allow func_type and all outputs, like p_signature, p_attachment and + * p_call_conv_raw to be NULL + */ void * wasm_native_resolve_symbol(const char *module_name, const char *field_name, const WASMType *func_type, const char **p_signature, @@ -264,10 +279,13 @@ wasm_native_resolve_symbol(const char *module_name, const char *field_name, node = node_next; } + if (!p_signature || !p_attachment || !p_call_conv_raw) + return func_ptr; + if (func_ptr) { if (signature && signature[0] != '\0') { /* signature is not empty, check its format */ - if (!check_symbol_signature(func_type, signature)) { + if (!func_type || !check_symbol_signature(func_type, signature)) { #if WASM_ENABLE_WAMR_COMPILER == 0 /* Output warning except running aot compiler */ LOG_WARNING("failed to check signature '%s' and resolve " @@ -383,7 +401,7 @@ wasm_native_init() || WASM_ENABLE_BASE_LIB != 0 || WASM_ENABLE_LIBC_EMCC != 0 \ || WASM_ENABLE_LIB_RATS != 0 || WASM_ENABLE_WASI_NN != 0 \ || WASM_ENABLE_APP_FRAMEWORK != 0 || WASM_ENABLE_LIBC_WASI != 0 \ - || WASM_ENABLE_LIB_PTHREAD != 0 + || WASM_ENABLE_LIB_PTHREAD != 0 || WASM_ENABLE_LIB_WASI_THREADS != 0 NativeSymbol *native_symbols; uint32 n_native_symbols; #endif @@ -438,6 +456,17 @@ wasm_native_init() goto fail; #endif +#if WASM_ENABLE_LIB_WASI_THREADS != 0 + if (!lib_wasi_threads_init()) + goto fail; + + n_native_symbols = get_lib_wasi_threads_export_apis(&native_symbols); + if (n_native_symbols > 0 + && !wasm_native_register_natives("wasi", native_symbols, + n_native_symbols)) + goto fail; +#endif + #if WASM_ENABLE_LIBC_EMCC != 0 n_native_symbols = get_libc_emcc_export_apis(&native_symbols); if (n_native_symbols > 0 @@ -458,7 +487,7 @@ wasm_native_init() n_native_symbols = get_wasi_nn_export_apis(&native_symbols); if (!wasm_native_register_natives("wasi_nn", native_symbols, n_native_symbols)) - return false; + goto fail; #endif return true; @@ -466,7 +495,7 @@ wasm_native_init() || WASM_ENABLE_BASE_LIB != 0 || WASM_ENABLE_LIBC_EMCC != 0 \ || WASM_ENABLE_LIB_RATS != 0 || WASM_ENABLE_WASI_NN != 0 \ || WASM_ENABLE_APP_FRAMEWORK != 0 || WASM_ENABLE_LIBC_WASI != 0 \ - || WASM_ENABLE_LIB_PTHREAD != 0 + || WASM_ENABLE_LIB_PTHREAD != 0 || WASM_ENABLE_LIB_WASI_THREADS != 0 fail: wasm_native_destroy(); return false; @@ -482,6 +511,10 @@ wasm_native_destroy() lib_pthread_destroy(); #endif +#if WASM_ENABLE_LIB_WASI_THREADS != 0 + lib_wasi_threads_destroy(); +#endif + node = g_native_symbols_list; while (node) { node_next = node->next; diff --git a/core/iwasm/common/wasm_runtime_common.c b/core/iwasm/common/wasm_runtime_common.c index 88dd1c19d..e92370c79 100644 --- a/core/iwasm/common/wasm_runtime_common.c +++ b/core/iwasm/common/wasm_runtime_common.c @@ -7,6 +7,7 @@ #include "bh_common.h" #include "bh_assert.h" #include "bh_log.h" +#include "wasm_native.h" #include "wasm_runtime_common.h" #include "wasm_memory.h" #if WASM_ENABLE_INTERP != 0 @@ -128,6 +129,12 @@ runtime_malloc(uint64 size, WASMModuleInstanceCommon *module_inst, static JitCompOptions jit_options = { 0 }; #endif +#if WASM_ENABLE_JIT != 0 +static LLVMJITOptions llvm_jit_options = { 3, 3 }; +#endif + +static RunningMode runtime_running_mode = Mode_Default; + #ifdef OS_ENABLE_HW_BOUND_CHECK /* The exec_env of thread local storage, set before calling function and used in signal handler, as we cannot get it from the argument @@ -187,7 +194,7 @@ runtime_signal_handler(void *sig_addr) else if (exec_env_tls->exce_check_guard_page <= (uint8 *)sig_addr && (uint8 *)sig_addr < exec_env_tls->exce_check_guard_page + page_size) { - bh_assert(wasm_get_exception(module_inst)); + bh_assert(wasm_copy_exception(module_inst, NULL)); os_longjmp(jmpbuf_node->jmpbuf, 1); } } @@ -243,7 +250,7 @@ runtime_exception_handler(EXCEPTION_POINTERS *exce_info) else if (exec_env_tls->exce_check_guard_page <= (uint8 *)sig_addr && (uint8 *)sig_addr < exec_env_tls->exce_check_guard_page + page_size) { - bh_assert(wasm_get_exception(module_inst)); + bh_assert(wasm_copy_exception(module_inst, NULL)); if (module_inst->module_type == Wasm_Module_Bytecode) { return EXCEPTION_CONTINUE_SEARCH; } @@ -514,6 +521,20 @@ wasm_runtime_destroy() wasm_runtime_memory_destroy(); } +RunningMode +wasm_runtime_get_default_running_mode(void) +{ + return runtime_running_mode; +} + +#if WASM_ENABLE_JIT != 0 +LLVMJITOptions +wasm_runtime_get_llvm_jit_options(void) +{ + return llvm_jit_options; +} +#endif + bool wasm_runtime_full_init(RuntimeInitArgs *init_args) { @@ -521,10 +542,20 @@ wasm_runtime_full_init(RuntimeInitArgs *init_args) &init_args->mem_alloc_option)) return false; + if (!wasm_runtime_set_default_running_mode(init_args->running_mode)) { + wasm_runtime_memory_destroy(); + return false; + } + #if WASM_ENABLE_FAST_JIT != 0 jit_options.code_cache_size = init_args->fast_jit_code_cache_size; #endif +#if WASM_ENABLE_JIT != 0 + llvm_jit_options.size_level = init_args->llvm_jit_size_level; + llvm_jit_options.opt_level = init_args->llvm_jit_opt_level; +#endif + if (!wasm_runtime_env_init()) { wasm_runtime_memory_destroy(); return false; @@ -554,6 +585,47 @@ wasm_runtime_full_init(RuntimeInitArgs *init_args) return true; } +bool +wasm_runtime_is_running_mode_supported(RunningMode running_mode) +{ + if (running_mode == Mode_Default) { + return true; + } + else if (running_mode == Mode_Interp) { +#if WASM_ENABLE_INTERP != 0 + return true; +#endif + } + else if (running_mode == Mode_Fast_JIT) { +#if WASM_ENABLE_FAST_JIT != 0 + return true; +#endif + } + else if (running_mode == Mode_LLVM_JIT) { +#if WASM_ENABLE_JIT != 0 + return true; +#endif + } + else if (running_mode == Mode_Multi_Tier_JIT) { +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ + && WASM_ENABLE_LAZY_JIT != 0 + return true; +#endif + } + + return false; +} + +bool +wasm_runtime_set_default_running_mode(RunningMode running_mode) +{ + if (wasm_runtime_is_running_mode_supported(running_mode)) { + runtime_running_mode = running_mode; + return true; + } + return false; +} + PackageType get_package_type(const uint8 *buf, uint32 size) { @@ -1171,6 +1243,41 @@ wasm_runtime_deinstantiate_internal(WASMModuleInstanceCommon *module_inst, #endif } +bool +wasm_runtime_set_running_mode(wasm_module_inst_t module_inst, + RunningMode running_mode) +{ +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) + return true; +#endif + +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + WASMModuleInstance *module_inst_interp = + (WASMModuleInstance *)module_inst; + + return wasm_set_running_mode(module_inst_interp, running_mode); + } +#endif + + return false; +} + +RunningMode +wasm_runtime_get_running_mode(wasm_module_inst_t module_inst) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + WASMModuleInstance *module_inst_interp = + (WASMModuleInstance *)module_inst; + return module_inst_interp->e->running_mode; + } +#endif + + return Mode_Default; +} + void wasm_runtime_deinstantiate(WASMModuleInstanceCommon *module_inst) { @@ -1399,6 +1506,22 @@ wasm_runtime_dump_mem_consumption(WASMExecEnv *exec_env) else os_printf("Total aux stack used: no enough info to profile\n"); + /* + * Report the native stack usage estimation. + * + * Unlike the aux stack above, we report the amount unused + * because we don't know the stack "bottom". + * + * Note that this is just about what the runtime itself observed. + * It doesn't cover host func implementations, signal handlers, etc. + */ + if (exec_env->native_stack_top_min != (void *)UINTPTR_MAX) + os_printf("Native stack left: %zd\n", + exec_env->native_stack_top_min + - exec_env->native_stack_boundary); + else + os_printf("Native stack left: no enough info to profile\n"); + os_printf("Total app heap used: %u\n", app_heap_peak_size); } #endif /* end of (WASM_ENABLE_MEMORY_PROFILING != 0) \ @@ -1743,6 +1866,33 @@ wasm_runtime_finalize_call_function(WASMExecEnv *exec_env, } #endif +static bool +clear_wasi_proc_exit_exception(WASMModuleInstanceCommon *module_inst_comm) +{ +#if WASM_ENABLE_LIBC_WASI != 0 + bool has_exception; + char exception[EXCEPTION_BUF_LEN]; + WASMModuleInstance *module_inst = (WASMModuleInstance *)module_inst_comm; + + bh_assert(module_inst_comm->module_type == Wasm_Module_Bytecode + || module_inst_comm->module_type == Wasm_Module_AoT); + + has_exception = wasm_copy_exception(module_inst, exception); + if (has_exception && !strcmp(exception, "Exception: wasi proc exit")) { + /* The "wasi proc exit" exception is thrown by native lib to + let wasm app exit, which is a normal behavior, we clear + the exception here. And just clear the exception of current + thread, don't call `wasm_set_exception(module_inst, NULL)` + which will clear the exception of all threads. */ + module_inst->cur_exception[0] = '\0'; + return true; + } + return false; +#else + return false; +#endif +} + bool wasm_runtime_call_wasm(WASMExecEnv *exec_env, WASMFunctionInstanceCommon *function, uint32 argc, @@ -1783,10 +1933,15 @@ wasm_runtime_call_wasm(WASMExecEnv *exec_env, param_argc, new_argv); #endif if (!ret) { - if (new_argv != argv) { - wasm_runtime_free(new_argv); + if (clear_wasi_proc_exit_exception(exec_env->module_inst)) { + ret = true; + } + else { + if (new_argv != argv) { + wasm_runtime_free(new_argv); + } + return false; } - return false; } #if WASM_ENABLE_REF_TYPES != 0 @@ -2150,11 +2305,41 @@ wasm_runtime_get_exec_env_singleton(WASMModuleInstanceCommon *module_inst_comm) void wasm_set_exception(WASMModuleInstance *module_inst, const char *exception) { - if (exception) + WASMExecEnv *exec_env = NULL; + +#if WASM_ENABLE_SHARED_MEMORY != 0 + WASMSharedMemNode *node = + wasm_module_get_shared_memory((WASMModuleCommon *)module_inst->module); + if (node) + os_mutex_lock(&node->shared_mem_lock); +#endif + if (exception) { snprintf(module_inst->cur_exception, sizeof(module_inst->cur_exception), "Exception: %s", exception); - else + } + else { module_inst->cur_exception[0] = '\0'; + } +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (node) + os_mutex_unlock(&node->shared_mem_lock); +#endif + +#if WASM_ENABLE_THREAD_MGR != 0 + exec_env = + wasm_clusters_search_exec_env((WASMModuleInstanceCommon *)module_inst); + if (exec_env) { + wasm_cluster_spread_exception(exec_env, exception ? false : true); + } +#if WASM_ENABLE_SHARED_MEMORY + if (exception) { + notify_stale_threads_on_exception( + (WASMModuleInstanceCommon *)module_inst); + } +#endif +#else + (void)exec_env; +#endif } /* clang-format off */ @@ -2176,9 +2361,7 @@ static const char *exception_msgs[] = { "wasm auxiliary stack underflow", /* EXCE_AUX_STACK_UNDERFLOW */ "out of bounds table access", /* EXCE_OUT_OF_BOUNDS_TABLE_ACCESS */ "wasm operand stack overflow", /* EXCE_OPERAND_STACK_OVERFLOW */ -#if WASM_ENABLE_FAST_JIT != 0 "failed to compile fast jit function", /* EXCE_FAILED_TO_COMPILE_FAST_JIT_FUNC */ -#endif "", /* EXCE_ALREADY_THROWN */ }; /* clang-format on */ @@ -2201,6 +2384,36 @@ wasm_get_exception(WASMModuleInstance *module_inst) return module_inst->cur_exception; } +bool +wasm_copy_exception(WASMModuleInstance *module_inst, char *exception_buf) +{ + bool has_exception = false; + +#if WASM_ENABLE_SHARED_MEMORY != 0 + WASMSharedMemNode *node = + wasm_module_get_shared_memory((WASMModuleCommon *)module_inst->module); + if (node) + os_mutex_lock(&node->shared_mem_lock); +#endif + if (module_inst->cur_exception[0] != '\0') { + /* NULL is passed if the caller is not interested in getting the + * exception content, but only in knowing if an exception has been + * raised + */ + if (exception_buf != NULL) + bh_memcpy_s(exception_buf, sizeof(module_inst->cur_exception), + module_inst->cur_exception, + sizeof(module_inst->cur_exception)); + has_exception = true; + } +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (node) + os_mutex_unlock(&node->shared_mem_lock); +#endif + + return has_exception; +} + void wasm_runtime_set_exception(WASMModuleInstanceCommon *module_inst_comm, const char *exception) @@ -2222,6 +2435,17 @@ wasm_runtime_get_exception(WASMModuleInstanceCommon *module_inst_comm) return wasm_get_exception(module_inst); } +bool +wasm_runtime_copy_exception(WASMModuleInstanceCommon *module_inst_comm, + char *exception_buf) +{ + WASMModuleInstance *module_inst = (WASMModuleInstance *)module_inst_comm; + + bh_assert(module_inst_comm->module_type == Wasm_Module_Bytecode + || module_inst_comm->module_type == Wasm_Module_AoT); + return wasm_copy_exception(module_inst, exception_buf); +} + void wasm_runtime_clear_exception(WASMModuleInstanceCommon *module_inst_comm) { @@ -3145,7 +3369,7 @@ wasm_runtime_invoke_native_raw(WASMExecEnv *exec_env, void *func_ptr, } } - ret = !wasm_runtime_get_exception(module) ? true : false; + ret = !wasm_runtime_copy_exception(module, NULL); fail: if (argv1 != argv_buf) @@ -3620,7 +3844,7 @@ wasm_runtime_invoke_native(WASMExecEnv *exec_env, void *func_ptr, } exec_env->attachment = NULL; - ret = !wasm_runtime_get_exception(module) ? true : false; + ret = !wasm_runtime_copy_exception(module, NULL); fail: if (argv1 != argv_buf) @@ -3834,7 +4058,7 @@ wasm_runtime_invoke_native(WASMExecEnv *exec_env, void *func_ptr, } exec_env->attachment = NULL; - ret = !wasm_runtime_get_exception(module) ? true : false; + ret = !wasm_runtime_copy_exception(module, NULL); fail: if (argv1 != argv_buf) @@ -4161,7 +4385,7 @@ wasm_runtime_invoke_native(WASMExecEnv *exec_env, void *func_ptr, } exec_env->attachment = NULL; - ret = !wasm_runtime_get_exception(module) ? true : false; + ret = !wasm_runtime_copy_exception(module, NULL); fail: if (argv1 != argv_buf) wasm_runtime_free(argv1); @@ -4176,9 +4400,11 @@ fail: || defined(BUILD_TARGET_RISCV64_LP64) */ bool -wasm_runtime_call_indirect(WASMExecEnv *exec_env, uint32 element_indices, +wasm_runtime_call_indirect(WASMExecEnv *exec_env, uint32 element_index, uint32 argc, uint32 argv[]) { + bool ret = false; + if (!wasm_runtime_exec_env_check(exec_env)) { LOG_ERROR("Invalid exec env stack info."); return false; @@ -4190,13 +4416,18 @@ wasm_runtime_call_indirect(WASMExecEnv *exec_env, uint32 element_indices, #if WASM_ENABLE_INTERP != 0 if (exec_env->module_inst->module_type == Wasm_Module_Bytecode) - return wasm_call_indirect(exec_env, 0, element_indices, argc, argv); + ret = wasm_call_indirect(exec_env, 0, element_index, argc, argv); #endif #if WASM_ENABLE_AOT != 0 if (exec_env->module_inst->module_type == Wasm_Module_AoT) - return aot_call_indirect(exec_env, 0, element_indices, argc, argv); + ret = aot_call_indirect(exec_env, 0, element_index, argc, argv); #endif - return false; + + if (!ret && clear_wasi_proc_exit_exception(exec_env->module_inst)) { + ret = true; + } + + return ret; } static void @@ -5155,3 +5386,24 @@ wasm_runtime_get_version(uint32_t *major, uint32_t *minor, uint32_t *patch) *minor = WAMR_VERSION_MINOR; *patch = WAMR_VERSION_PATCH; } + +bool +wasm_runtime_is_import_func_linked(const char *module_name, + const char *func_name) +{ + return wasm_native_resolve_symbol(module_name, func_name, NULL, NULL, NULL, + NULL); +} + +bool +wasm_runtime_is_import_global_linked(const char *module_name, + const char *global_name) +{ +#if WASM_ENABLE_LIBC_BUILTIN != 0 + WASMGlobalImport global = { 0 }; + return wasm_native_lookup_libc_builtin_global(module_name, global_name, + &global); +#else + return false; +#endif +} diff --git a/core/iwasm/common/wasm_runtime_common.h b/core/iwasm/common/wasm_runtime_common.h index 3e7f3725d..b20e12ecf 100644 --- a/core/iwasm/common/wasm_runtime_common.h +++ b/core/iwasm/common/wasm_runtime_common.h @@ -25,6 +25,9 @@ extern "C" { #endif +/* Internal use for setting default running mode */ +#define Mode_Default 0 + #if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS != 0 #define PUT_I64_TO_ADDR(addr, value) \ @@ -413,6 +416,13 @@ typedef struct wasm_frame_t { const char *func_name_wp; } WASMCApiFrame; +#ifdef WASM_ENABLE_JIT +typedef struct LLVMJITOptions { + uint32 opt_level; + uint32 size_level; +} LLVMJITOptions; +#endif + #ifdef OS_ENABLE_HW_BOUND_CHECK /* Signal info passing to interp/aot signal handler */ typedef struct WASMSignalInfo { @@ -437,10 +447,28 @@ wasm_runtime_get_exec_env_tls(void); WASM_RUNTIME_API_EXTERN bool wasm_runtime_init(void); +/* Internal API */ +RunningMode +wasm_runtime_get_default_running_mode(void); + +#if WASM_ENABLE_JIT != 0 +/* Internal API */ +LLVMJITOptions +wasm_runtime_get_llvm_jit_options(void); +#endif + /* See wasm_export.h for description */ WASM_RUNTIME_API_EXTERN bool wasm_runtime_full_init(RuntimeInitArgs *init_args); +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_is_running_mode_supported(RunningMode running_mode); + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_set_default_running_mode(RunningMode running_mode); + /* See wasm_export.h for description */ WASM_RUNTIME_API_EXTERN void wasm_runtime_destroy(void); @@ -484,6 +512,15 @@ wasm_runtime_instantiate(WASMModuleCommon *module, uint32 stack_size, uint32 heap_size, char *error_buf, uint32 error_buf_size); +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_set_running_mode(wasm_module_inst_t module_inst, + RunningMode running_mode); + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN RunningMode +wasm_runtime_get_running_mode(wasm_module_inst_t module_inst); + /* See wasm_export.h for description */ WASM_RUNTIME_API_EXTERN void wasm_runtime_deinstantiate(WASMModuleInstanceCommon *module_inst); @@ -578,6 +615,11 @@ wasm_runtime_call_wasm_v(WASMExecEnv *exec_env, uint32 num_results, wasm_val_t *results, uint32 num_args, ...); +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_call_indirect(WASMExecEnv *exec_env, uint32 element_index, + uint32 argc, uint32 argv[]); + #if WASM_ENABLE_DEBUG_INTERP != 0 /* See wasm_export.h for description */ WASM_RUNTIME_API_EXTERN uint32 @@ -589,27 +631,6 @@ WASM_RUNTIME_API_EXTERN uint32 wasm_runtime_start_debug_instance(WASMExecEnv *exec_env); #endif -/** - * Call a function reference of a given WASM runtime instance with - * arguments. - * - * @param exec_env the execution environment to call the function - * which must be created from wasm_create_exec_env() - * @param element_indices the function ference indicies, usually - * prvovided by the caller of a registed native function - * @param argc the number of arguments - * @param argv the arguments. If the function method has return value, - * the first (or first two in case 64-bit return value) element of - * argv stores the return value of the called WASM function after this - * function returns. - * - * @return true if success, false otherwise and exception will be thrown, - * the caller can call wasm_runtime_get_exception to get exception info. - */ -bool -wasm_runtime_call_indirect(WASMExecEnv *exec_env, uint32 element_indices, - uint32 argc, uint32 argv[]); - bool wasm_runtime_create_exec_env_singleton(WASMModuleInstanceCommon *module_inst); @@ -956,6 +977,14 @@ void wasm_runtime_destroy_custom_sections(WASMCustomSection *section_list); #endif +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_is_import_func_linked(const char *module_name, + const char *func_name); + +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_is_import_global_linked(const char *module_name, + const char *global_name); + #ifdef __cplusplus } #endif diff --git a/core/iwasm/common/wasm_shared_memory.c b/core/iwasm/common/wasm_shared_memory.c index b2b63de5c..eb66f2f09 100644 --- a/core/iwasm/common/wasm_shared_memory.c +++ b/core/iwasm/common/wasm_shared_memory.c @@ -30,8 +30,14 @@ typedef struct AtomicWaitNode { korp_cond wait_cond; } AtomicWaitNode; +typedef struct AtomicWaitAddressArgs { + uint32 index; + void **addr; +} AtomicWaitAddressArgs; + /* Atomic wait map */ static HashMap *wait_map; +static korp_mutex wait_map_lock; static uint32 wait_address_hash(void *address); @@ -47,11 +53,18 @@ wasm_shared_memory_init() { if (os_mutex_init(&shared_memory_list_lock) != 0) return false; + + if (os_mutex_init(&wait_map_lock) != 0) { + os_mutex_destroy(&shared_memory_list_lock); + return false; + } + /* wait map not exists, create new map */ if (!(wait_map = bh_hash_map_create(32, true, (HashFunc)wait_address_hash, (KeyEqualFunc)wait_address_equal, NULL, destroy_wait_info))) { os_mutex_destroy(&shared_memory_list_lock); + os_mutex_destroy(&wait_map_lock); return false; } @@ -62,6 +75,7 @@ void wasm_shared_memory_destroy() { os_mutex_destroy(&shared_memory_list_lock); + os_mutex_destroy(&wait_map_lock); if (wait_map) { bh_hash_map_destroy(wait_map); } @@ -87,6 +101,61 @@ search_module(WASMModuleCommon *module) return NULL; } +static void +wait_map_address_count_callback(void *key, void *value, + void *p_total_elem_count) +{ + *(uint32 *)p_total_elem_count = *(uint32 *)p_total_elem_count + 1; +} + +static void +create_list_of_waiter_addresses(void *key, void *value, void *user_data) +{ + AtomicWaitAddressArgs *data = (AtomicWaitAddressArgs *)user_data; + data->addr[data->index++] = key; +} + +void +notify_stale_threads_on_exception(WASMModuleInstanceCommon *module_inst) +{ + AtomicWaitAddressArgs args = { 0 }; + uint32 i = 0, total_elem_count = 0; + uint64 total_elem_count_size = 0; + + os_mutex_lock(&wait_map_lock); /* Make the two traversals atomic */ + + /* count number of addresses in wait_map */ + bh_hash_map_traverse(wait_map, wait_map_address_count_callback, + (void *)&total_elem_count); + + if (!total_elem_count) { + os_mutex_unlock(&wait_map_lock); + return; + } + + /* allocate memory */ + total_elem_count_size = (uint64)sizeof(void *) * total_elem_count; + if (total_elem_count_size >= UINT32_MAX + || !(args.addr = wasm_runtime_malloc((uint32)total_elem_count_size))) { + LOG_ERROR( + "failed to allocate memory for list of atomic wait addresses"); + os_mutex_unlock(&wait_map_lock); + return; + } + + /* set values in list of addresses */ + bh_hash_map_traverse(wait_map, create_list_of_waiter_addresses, &args); + os_mutex_unlock(&wait_map_lock); + + /* notify */ + for (i = 0; i < args.index; i++) { + wasm_runtime_atomic_notify(module_inst, args.addr[i], UINT32_MAX); + } + + /* free memory allocated to args data */ + wasm_runtime_free(args.addr); +} + WASMSharedMemNode * wasm_module_get_shared_memory(WASMModuleCommon *module) { @@ -97,13 +166,13 @@ int32 shared_memory_inc_reference(WASMModuleCommon *module) { WASMSharedMemNode *node = search_module(module); + uint32 ref_count = -1; if (node) { os_mutex_lock(&node->lock); - node->ref_count++; + ref_count = ++node->ref_count; os_mutex_unlock(&node->lock); - return node->ref_count; } - return -1; + return ref_count; } int32 @@ -120,6 +189,7 @@ shared_memory_dec_reference(WASMModuleCommon *module) bh_list_remove(shared_memory_list, node); os_mutex_unlock(&shared_memory_list_lock); + os_mutex_destroy(&node->shared_mem_lock); os_mutex_destroy(&node->lock); wasm_runtime_free(node); } @@ -148,7 +218,14 @@ shared_memory_set_memory_inst(WASMModuleCommon *module, node->module = module; node->memory_inst = memory; node->ref_count = 1; + + if (os_mutex_init(&node->shared_mem_lock) != 0) { + wasm_runtime_free(node); + return NULL; + } + if (os_mutex_init(&node->lock) != 0) { + os_mutex_destroy(&node->shared_mem_lock); wasm_runtime_free(node); return NULL; } @@ -208,9 +285,11 @@ notify_wait_list(bh_list *wait_list, uint32 count) bh_assert(node); next = bh_list_elem_next(node); + os_mutex_lock(&node->wait_lock); node->status = S_NOTIFIED; /* wakeup */ os_cond_signal(&node->wait_cond); + os_mutex_unlock(&node->wait_lock); node = next; } @@ -224,13 +303,13 @@ acquire_wait_info(void *address, bool create) AtomicWaitInfo *wait_info = NULL; bh_list_status ret; - os_mutex_lock(&shared_memory_list_lock); + os_mutex_lock(&wait_map_lock); /* Make find + insert atomic */ if (address) wait_info = (AtomicWaitInfo *)bh_hash_map_find(wait_map, address); if (!create) { - os_mutex_unlock(&shared_memory_list_lock); + os_mutex_unlock(&wait_map_lock); return wait_info; } @@ -257,7 +336,7 @@ acquire_wait_info(void *address, bool create) } } - os_mutex_unlock(&shared_memory_list_lock); + os_mutex_unlock(&wait_map_lock); bh_assert(wait_info); (void)ret; @@ -270,7 +349,7 @@ fail2: wasm_runtime_free(wait_info); fail1: - os_mutex_unlock(&shared_memory_list_lock); + os_mutex_unlock(&wait_map_lock); return NULL; } @@ -297,17 +376,16 @@ destroy_wait_info(void *wait_info) } } -static void -release_wait_info(HashMap *wait_map_, AtomicWaitInfo *wait_info, void *address) +static bool +map_remove_wait_info(HashMap *wait_map_, AtomicWaitInfo *wait_info, + void *address) { - os_mutex_lock(&shared_memory_list_lock); - - if (wait_info->wait_list->len == 0) { - bh_hash_map_remove(wait_map_, address, NULL, NULL); - destroy_wait_info(wait_info); + if (wait_info->wait_list->len > 0) { + return false; } - os_mutex_unlock(&shared_memory_list_lock); + bh_hash_map_remove(wait_map_, address, NULL, NULL); + return true; } uint32 @@ -317,11 +395,16 @@ wasm_runtime_atomic_wait(WASMModuleInstanceCommon *module, void *address, WASMModuleInstance *module_inst = (WASMModuleInstance *)module; AtomicWaitInfo *wait_info; AtomicWaitNode *wait_node; - bool check_ret, is_timeout; + WASMSharedMemNode *node; + bool check_ret, is_timeout, no_wait, removed_from_map; bh_assert(module->module_type == Wasm_Module_Bytecode || module->module_type == Wasm_Module_AoT); + if (wasm_copy_exception(module_inst, NULL)) { + return -1; + } + /* Currently we have only one memory instance */ if (!module_inst->memories[0]->is_shared) { wasm_runtime_set_exception(module, "expected shared memory"); @@ -343,11 +426,13 @@ wasm_runtime_atomic_wait(WASMModuleInstanceCommon *module, void *address, return -1; } - os_mutex_lock(&wait_info->wait_list_lock); + node = search_module((WASMModuleCommon *)module_inst->module); + os_mutex_lock(&node->shared_mem_lock); + no_wait = (!wait64 && *(uint32 *)address != (uint32)expect) + || (wait64 && *(uint64 *)address != expect); + os_mutex_unlock(&node->shared_mem_lock); - if ((!wait64 && *(uint32 *)address != (uint32)expect) - || (wait64 && *(uint64 *)address != expect)) { - os_mutex_unlock(&wait_info->wait_list_lock); + if (no_wait) { return 1; } else { @@ -355,33 +440,29 @@ wasm_runtime_atomic_wait(WASMModuleInstanceCommon *module, void *address, if (!(wait_node = wasm_runtime_malloc(sizeof(AtomicWaitNode)))) { wasm_runtime_set_exception(module, "failed to create wait node"); - os_mutex_unlock(&wait_info->wait_list_lock); return -1; } memset(wait_node, 0, sizeof(AtomicWaitNode)); if (0 != os_mutex_init(&wait_node->wait_lock)) { wasm_runtime_free(wait_node); - os_mutex_unlock(&wait_info->wait_list_lock); return -1; } if (0 != os_cond_init(&wait_node->wait_cond)) { os_mutex_destroy(&wait_node->wait_lock); wasm_runtime_free(wait_node); - os_mutex_unlock(&wait_info->wait_list_lock); return -1; } wait_node->status = S_WAITING; - + os_mutex_lock(&wait_info->wait_list_lock); ret = bh_list_insert(wait_info->wait_list, wait_node); + os_mutex_unlock(&wait_info->wait_list_lock); bh_assert(ret == BH_LIST_SUCCESS); (void)ret; } - os_mutex_unlock(&wait_info->wait_list_lock); - /* condition wait start */ os_mutex_lock(&wait_node->wait_lock); @@ -389,22 +470,27 @@ wasm_runtime_atomic_wait(WASMModuleInstanceCommon *module, void *address, timeout < 0 ? BHT_WAIT_FOREVER : (uint64)timeout / 1000); + is_timeout = wait_node->status == S_WAITING ? true : false; os_mutex_unlock(&wait_node->wait_lock); - /* Check the wait node status */ + os_mutex_lock(&node->shared_mem_lock); os_mutex_lock(&wait_info->wait_list_lock); + check_ret = is_wait_node_exists(wait_info->wait_list, wait_node); bh_assert(check_ret); - is_timeout = wait_node->status == S_WAITING ? true : false; - + /* Remove wait node */ bh_list_remove(wait_info->wait_list, wait_node); os_mutex_destroy(&wait_node->wait_lock); os_cond_destroy(&wait_node->wait_cond); wasm_runtime_free(wait_node); - os_mutex_unlock(&wait_info->wait_list_lock); - release_wait_info(wait_map, wait_info, address); + /* Release wait info if no wait nodes attached */ + removed_from_map = map_remove_wait_info(wait_map, wait_info, address); + os_mutex_unlock(&wait_info->wait_list_lock); + if (removed_from_map) + destroy_wait_info(wait_info); + os_mutex_unlock(&node->shared_mem_lock); (void)check_ret; return is_timeout ? 2 : 0; @@ -417,12 +503,22 @@ wasm_runtime_atomic_notify(WASMModuleInstanceCommon *module, void *address, WASMModuleInstance *module_inst = (WASMModuleInstance *)module; uint32 notify_result; AtomicWaitInfo *wait_info; + WASMSharedMemNode *node; + bool out_of_bounds; bh_assert(module->module_type == Wasm_Module_Bytecode || module->module_type == Wasm_Module_AoT); - if ((uint8 *)address < module_inst->memories[0]->memory_data - || (uint8 *)address + 4 > module_inst->memories[0]->memory_data_end) { + node = search_module((WASMModuleCommon *)module_inst->module); + if (node) + os_mutex_lock(&node->shared_mem_lock); + out_of_bounds = + ((uint8 *)address < module_inst->memories[0]->memory_data + || (uint8 *)address + 4 > module_inst->memories[0]->memory_data_end); + + if (out_of_bounds) { + if (node) + os_mutex_unlock(&node->shared_mem_lock); wasm_runtime_set_exception(module, "out of bounds memory access"); return -1; } @@ -430,12 +526,18 @@ wasm_runtime_atomic_notify(WASMModuleInstanceCommon *module, void *address, wait_info = acquire_wait_info(address, false); /* Nobody wait on this address */ - if (!wait_info) + if (!wait_info) { + if (node) + os_mutex_unlock(&node->shared_mem_lock); return 0; + } os_mutex_lock(&wait_info->wait_list_lock); notify_result = notify_wait_list(wait_info->wait_list, count); os_mutex_unlock(&wait_info->wait_list_lock); + if (node) + os_mutex_unlock(&node->shared_mem_lock); + return notify_result; } diff --git a/core/iwasm/common/wasm_shared_memory.h b/core/iwasm/common/wasm_shared_memory.h index bc6f8945c..98683f32b 100644 --- a/core/iwasm/common/wasm_shared_memory.h +++ b/core/iwasm/common/wasm_shared_memory.h @@ -26,6 +26,8 @@ typedef struct WASMSharedMemNode { WASMModuleCommon *module; /* The memory information */ WASMMemoryInstanceCommon *memory_inst; + /* Lock used for atomic operations */ + korp_mutex shared_mem_lock; /* reference count */ uint32 ref_count; @@ -37,6 +39,9 @@ wasm_shared_memory_init(); void wasm_shared_memory_destroy(); +void +notify_stale_threads_on_exception(WASMModuleInstanceCommon *module); + WASMSharedMemNode * wasm_module_get_shared_memory(WASMModuleCommon *module); diff --git a/core/iwasm/compilation/aot.h b/core/iwasm/compilation/aot.h index c7989851c..c67251a6f 100644 --- a/core/iwasm/compilation/aot.h +++ b/core/iwasm/compilation/aot.h @@ -149,6 +149,7 @@ typedef struct AOTImportGlobal { uint32 data_offset; /* global data after linked */ WASMValue global_data_linked; + bool is_linked; } AOTImportGlobal; /** diff --git a/core/iwasm/compilation/aot_compiler.c b/core/iwasm/compilation/aot_compiler.c index 925371b11..06235fe31 100644 --- a/core/iwasm/compilation/aot_compiler.c +++ b/core/iwasm/compilation/aot_compiler.c @@ -1240,6 +1240,8 @@ aot_compile_func(AOTCompContext *comp_ctx, uint32 func_index) case WASM_OP_ATOMIC_FENCE: /* Skip memory index */ frame_ip++; + if (!aot_compiler_op_atomic_fence(comp_ctx, func_ctx)) + return false; break; case WASM_OP_ATOMIC_I32_LOAD: bytes = 4; diff --git a/core/iwasm/compilation/aot_compiler.h b/core/iwasm/compilation/aot_compiler.h index d5e45f28b..e6031ab89 100644 --- a/core/iwasm/compilation/aot_compiler.h +++ b/core/iwasm/compilation/aot_compiler.h @@ -259,6 +259,7 @@ check_type_compatible(uint8 src_type, uint8 dst_type) #define I32_SIX LLVM_CONST(i32_six) #define I32_SEVEN LLVM_CONST(i32_seven) #define I32_EIGHT LLVM_CONST(i32_eight) +#define I32_NINE LLVM_CONST(i32_nine) #define I32_NEG_ONE LLVM_CONST(i32_neg_one) #define I64_NEG_ONE LLVM_CONST(i64_neg_one) #define I32_MIN LLVM_CONST(i32_min) diff --git a/core/iwasm/compilation/aot_emit_conversion.c b/core/iwasm/compilation/aot_emit_conversion.c index 563193802..c3dfa6bf1 100644 --- a/core/iwasm/compilation/aot_emit_conversion.c +++ b/core/iwasm/compilation/aot_emit_conversion.c @@ -9,6 +9,36 @@ #include "../aot/aot_intrinsic.h" #include "../aot/aot_runtime.h" +static LLVMValueRef +call_fcmp_intrinsic(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + enum AOTFloatCond cond, LLVMRealPredicate op, + LLVMValueRef lhs, LLVMValueRef rhs, LLVMTypeRef src_type, + const char *name) +{ + LLVMValueRef res = NULL; + if (comp_ctx->disable_llvm_intrinsics + && aot_intrinsic_check_capability( + comp_ctx, src_type == F32_TYPE ? "f32_cmp" : "f64_cmp")) { + LLVMTypeRef param_types[3]; + LLVMValueRef opcond = LLVMConstInt(I32_TYPE, cond, true); + param_types[0] = I32_TYPE; + param_types[1] = src_type; + param_types[2] = src_type; + res = aot_call_llvm_intrinsic( + comp_ctx, func_ctx, src_type == F32_TYPE ? "f32_cmp" : "f64_cmp", + I32_TYPE, param_types, 3, opcond, lhs, rhs); + if (!res) { + goto fail; + } + res = LLVMBuildIntCast(comp_ctx->builder, res, INT1_TYPE, "bit_cast"); + } + else { + res = LLVMBuildFCmp(comp_ctx->builder, op, lhs, rhs, name); + } +fail: + return res; +} + static bool trunc_float_to_int(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, LLVMValueRef operand, LLVMTypeRef src_type, @@ -18,26 +48,8 @@ trunc_float_to_int(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, LLVMBasicBlockRef check_nan_succ, check_overflow_succ; LLVMValueRef is_less, is_greater, res; - if (comp_ctx->disable_llvm_intrinsics - && aot_intrinsic_check_capability( - comp_ctx, src_type == F32_TYPE ? "f32_cmp" : "f64_cmp")) { - LLVMTypeRef param_types[3]; - LLVMValueRef opcond = LLVMConstInt(I32_TYPE, FLOAT_UNO, true); - param_types[0] = I32_TYPE; - param_types[1] = src_type; - param_types[2] = src_type; - res = aot_call_llvm_intrinsic( - comp_ctx, func_ctx, src_type == F32_TYPE ? "f32_cmp" : "f64_cmp", - I32_TYPE, param_types, 3, opcond, operand, operand); - if (!res) { - goto fail; - } - res = LLVMBuildIntCast(comp_ctx->builder, res, INT1_TYPE, "bit_cast"); - } - else { - res = LLVMBuildFCmp(comp_ctx->builder, LLVMRealUNO, operand, operand, - "fcmp_is_nan"); - } + res = call_fcmp_intrinsic(comp_ctx, func_ctx, FLOAT_UNO, LLVMRealUNO, + operand, operand, src_type, "fcmp_is_nan"); if (!res) { aot_set_last_error("llvm build fcmp failed."); @@ -58,54 +70,18 @@ trunc_float_to_int(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, check_nan_succ))) goto fail; - if (comp_ctx->disable_llvm_intrinsics - && aot_intrinsic_check_capability( - comp_ctx, src_type == F32_TYPE ? "f32_cmp" : "f64_cmp")) { - LLVMTypeRef param_types[3]; - LLVMValueRef opcond = LLVMConstInt(I32_TYPE, FLOAT_LE, true); - param_types[0] = I32_TYPE; - param_types[1] = src_type; - param_types[2] = src_type; - is_less = aot_call_llvm_intrinsic( - comp_ctx, func_ctx, src_type == F32_TYPE ? "f32_cmp" : "f64_cmp", - I32_TYPE, param_types, 3, opcond, operand, min_value); - if (!is_less) { - goto fail; - } - is_less = - LLVMBuildIntCast(comp_ctx->builder, is_less, INT1_TYPE, "bit_cast"); - } - else { - is_less = LLVMBuildFCmp(comp_ctx->builder, LLVMRealOLE, operand, - min_value, "fcmp_min_value"); - } + is_less = + call_fcmp_intrinsic(comp_ctx, func_ctx, FLOAT_LE, LLVMRealOLE, operand, + min_value, src_type, "fcmp_min_value"); if (!is_less) { aot_set_last_error("llvm build fcmp failed."); goto fail; } - if (comp_ctx->disable_llvm_intrinsics - && aot_intrinsic_check_capability( - comp_ctx, src_type == F32_TYPE ? "f32_cmp" : "f64_cmp")) { - LLVMTypeRef param_types[3]; - LLVMValueRef opcond = LLVMConstInt(I32_TYPE, FLOAT_GE, true); - param_types[0] = I32_TYPE; - param_types[1] = src_type; - param_types[2] = src_type; - is_greater = aot_call_llvm_intrinsic( - comp_ctx, func_ctx, src_type == F32_TYPE ? "f32_cmp" : "f64_cmp", - I32_TYPE, param_types, 3, opcond, operand, max_value); - if (!is_greater) { - goto fail; - } - is_greater = LLVMBuildIntCast(comp_ctx->builder, is_greater, INT1_TYPE, - "bit_cast"); - } - else { - is_greater = LLVMBuildFCmp(comp_ctx->builder, LLVMRealOGE, operand, - max_value, "fcmp_min_value"); - } + is_greater = + call_fcmp_intrinsic(comp_ctx, func_ctx, FLOAT_GE, LLVMRealOGE, operand, + max_value, src_type, "fcmp_min_value"); if (!is_greater) { aot_set_last_error("llvm build fcmp failed."); @@ -183,8 +159,9 @@ trunc_sat_float_to_int(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, LLVMValueRef zero = (dest_type == I32_TYPE) ? I32_ZERO : I64_ZERO; LLVMValueRef vmin, vmax; - if (!(res = LLVMBuildFCmp(comp_ctx->builder, LLVMRealUNO, operand, operand, - "fcmp_is_nan"))) { + if (!(res = + call_fcmp_intrinsic(comp_ctx, func_ctx, FLOAT_UNO, LLVMRealUNO, + operand, operand, src_type, "fcmp_is_nan"))) { aot_set_last_error("llvm build fcmp failed."); goto fail; } @@ -212,8 +189,9 @@ trunc_sat_float_to_int(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, /* Start to translate check_nan_succ block */ LLVMPositionBuilderAtEnd(comp_ctx->builder, check_nan_succ); - if (!(is_less = LLVMBuildFCmp(comp_ctx->builder, LLVMRealOLE, operand, - min_value, "fcmp_min_value"))) { + if (!(is_less = call_fcmp_intrinsic(comp_ctx, func_ctx, FLOAT_LE, + LLVMRealOLE, operand, min_value, + src_type, "fcmp_min_value"))) { aot_set_last_error("llvm build fcmp failed."); goto fail; } @@ -232,8 +210,9 @@ trunc_sat_float_to_int(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, /* Start to translate check_less_succ block */ LLVMPositionBuilderAtEnd(comp_ctx->builder, check_less_succ); - if (!(is_greater = LLVMBuildFCmp(comp_ctx->builder, LLVMRealOGE, operand, - max_value, "fcmp_max_value"))) { + if (!(is_greater = call_fcmp_intrinsic(comp_ctx, func_ctx, FLOAT_GE, + LLVMRealOGE, operand, max_value, + src_type, "fcmp_max_value"))) { aot_set_last_error("llvm build fcmp failed."); goto fail; } diff --git a/core/iwasm/compilation/aot_emit_function.c b/core/iwasm/compilation/aot_emit_function.c index d6a5a4aba..9ba8baa24 100644 --- a/core/iwasm/compilation/aot_emit_function.c +++ b/core/iwasm/compilation/aot_emit_function.c @@ -366,6 +366,87 @@ fail: #endif /* end of (WASM_ENABLE_DUMP_CALL_STACK != 0) \ || (WASM_ENABLE_PERF_PROFILING != 0) */ +static bool +record_stack_usage(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 callee_cell_num) +{ + LLVMBasicBlockRef block_curr = LLVMGetInsertBlock(comp_ctx->builder); + LLVMBasicBlockRef block_update; + LLVMBasicBlockRef block_after_update; + LLVMValueRef callee_local_size, new_sp, cmp; + LLVMValueRef native_stack_top_min; + LLVMTypeRef ptrdiff_type; + if (comp_ctx->pointer_size == sizeof(uint64_t)) { + ptrdiff_type = I64_TYPE; + } + else { + ptrdiff_type = I32_TYPE; + } + + /* + * new_sp = last_alloca - callee_local_size; + * if (*native_stack_top_min_addr > new_sp) { + * *native_stack_top_min_addr = new_sp; + * } + */ + + if (!(callee_local_size = LLVMConstInt( + ptrdiff_type, -(int64_t)callee_cell_num * 4, true))) { + aot_set_last_error("llvm build const failed."); + return false; + } + if (!(new_sp = LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, + func_ctx->last_alloca, + &callee_local_size, 1, "new_sp"))) { + aot_set_last_error("llvm build gep failed"); + return false; + } + if (!(native_stack_top_min = LLVMBuildLoad2( + comp_ctx->builder, OPQ_PTR_TYPE, + func_ctx->native_stack_top_min_addr, "native_stack_top_min"))) { + aot_set_last_error("llvm build load failed"); + return false; + } + if (!(cmp = LLVMBuildICmp(comp_ctx->builder, LLVMIntULT, new_sp, + native_stack_top_min, "cmp"))) { + aot_set_last_error("llvm build icmp failed."); + return false; + } + + if (!(block_update = LLVMAppendBasicBlockInContext( + comp_ctx->context, func_ctx->func, "block_update"))) { + aot_set_last_error("llvm add basic block failed."); + return false; + } + if (!(block_after_update = LLVMAppendBasicBlockInContext( + comp_ctx->context, func_ctx->func, "block_after_update"))) { + aot_set_last_error("llvm add basic block failed."); + return false; + } + LLVMMoveBasicBlockAfter(block_update, block_curr); + LLVMMoveBasicBlockAfter(block_after_update, block_update); + + if (!LLVMBuildCondBr(comp_ctx->builder, cmp, block_update, + block_after_update)) { + aot_set_last_error("llvm build cond br failed."); + return false; + } + + LLVMPositionBuilderAtEnd(comp_ctx->builder, block_update); + if (!LLVMBuildStore(comp_ctx->builder, new_sp, + func_ctx->native_stack_top_min_addr)) { + aot_set_last_error("llvm build store failed"); + return false; + } + if (!LLVMBuildBr(comp_ctx->builder, block_after_update)) { + aot_set_last_error("llvm build br failed."); + return false; + } + + LLVMPositionBuilderAtEnd(comp_ctx->builder, block_after_update); + return true; +} + static bool check_stack_boundary(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, uint32 callee_cell_num) @@ -409,6 +490,19 @@ check_stack_boundary(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, return true; } +static bool +check_stack(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 callee_cell_num) +{ + if (comp_ctx->enable_stack_estimation + && !record_stack_usage(comp_ctx, func_ctx, callee_cell_num)) + return false; + if (comp_ctx->enable_stack_bound_check + && !check_stack_boundary(comp_ctx, func_ctx, callee_cell_num)) + return false; + return true; +} + /** * Check whether the app address and its buffer are inside the linear memory, * if no, throw exception @@ -852,8 +946,7 @@ aot_compile_op_call(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, callee_cell_num = aot_func->param_cell_num + aot_func->local_cell_num + 1; - if (comp_ctx->enable_stack_bound_check - && !check_stack_boundary(comp_ctx, func_ctx, callee_cell_num)) + if (!check_stack(comp_ctx, func_ctx, callee_cell_num)) goto fail; #if LLVM_VERSION_MAJOR >= 14 @@ -906,6 +999,14 @@ aot_compile_op_call(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, } #endif +#if WASM_ENABLE_THREAD_MGR != 0 + /* Insert suspend check point */ + if (comp_ctx->enable_thread_mgr) { + if (!check_suspend_flags(comp_ctx, func_ctx)) + goto fail; + } +#endif + ret = true; fail: if (param_types) @@ -1467,12 +1568,11 @@ aot_compile_op_call_indirect(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, /* Translate call non-import block */ LLVMPositionBuilderAtEnd(comp_ctx->builder, block_call_non_import); - if (comp_ctx->enable_stack_bound_check - && !check_stack_boundary(comp_ctx, func_ctx, - param_cell_num + ext_cell_num - + 1 - /* Reserve some local variables */ - + 16)) + if (!check_stack(comp_ctx, func_ctx, + param_cell_num + ext_cell_num + + 1 + /* Reserve some local variables */ + + 16)) goto fail; /* Load function pointer */ @@ -1553,6 +1653,14 @@ aot_compile_op_call_indirect(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, } #endif +#if WASM_ENABLE_THREAD_MGR != 0 + /* Insert suspend check point */ + if (comp_ctx->enable_thread_mgr) { + if (!check_suspend_flags(comp_ctx, func_ctx)) + goto fail; + } +#endif + ret = true; fail: diff --git a/core/iwasm/compilation/aot_emit_memory.c b/core/iwasm/compilation/aot_emit_memory.c index 7224d9f5d..4da4cc807 100644 --- a/core/iwasm/compilation/aot_emit_memory.c +++ b/core/iwasm/compilation/aot_emit_memory.c @@ -7,6 +7,7 @@ #include "aot_emit_exception.h" #include "../aot/aot_runtime.h" #include "aot_intrinsic.h" +#include "aot_emit_control.h" #define BUILD_ICMP(op, left, right, res, name) \ do { \ @@ -1344,7 +1345,7 @@ aot_compile_op_atomic_wait(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, return false; } - BUILD_ICMP(LLVMIntSGT, ret_value, I32_ZERO, cmp, "atomic_wait_ret"); + BUILD_ICMP(LLVMIntNE, ret_value, I32_NEG_ONE, cmp, "atomic_wait_ret"); ADD_BASIC_BLOCK(wait_fail, "atomic_wait_fail"); ADD_BASIC_BLOCK(wait_success, "wait_success"); @@ -1368,6 +1369,14 @@ aot_compile_op_atomic_wait(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, PUSH_I32(ret_value); +#if WASM_ENABLE_THREAD_MGR != 0 + /* Insert suspend check point */ + if (comp_ctx->enable_thread_mgr) { + if (!check_suspend_flags(comp_ctx, func_ctx)) + return false; + } +#endif + return true; fail: return false; @@ -1414,4 +1423,13 @@ fail: return false; } +bool +aot_compiler_op_atomic_fence(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + return LLVMBuildFence(comp_ctx->builder, + LLVMAtomicOrderingSequentiallyConsistent, false, "") + ? true + : false; +} + #endif /* end of WASM_ENABLE_SHARED_MEMORY */ diff --git a/core/iwasm/compilation/aot_emit_memory.h b/core/iwasm/compilation/aot_emit_memory.h index 2c39f725b..e49582e3c 100644 --- a/core/iwasm/compilation/aot_emit_memory.h +++ b/core/iwasm/compilation/aot_emit_memory.h @@ -97,6 +97,10 @@ bool aot_compiler_op_atomic_notify(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, uint32 align, uint32 offset, uint32 bytes); + +bool +aot_compiler_op_atomic_fence(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); #endif #ifdef __cplusplus diff --git a/core/iwasm/compilation/aot_llvm.c b/core/iwasm/compilation/aot_llvm.c index 889ab259c..27550560f 100644 --- a/core/iwasm/compilation/aot_llvm.c +++ b/core/iwasm/compilation/aot_llvm.c @@ -286,6 +286,21 @@ create_native_stack_bound(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) return true; } +static bool +create_native_stack_top_min(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + LLVMValueRef offset = I32_NINE; + + if (!(func_ctx->native_stack_top_min_addr = LLVMBuildInBoundsGEP2( + comp_ctx->builder, OPQ_PTR_TYPE, func_ctx->exec_env, &offset, 1, + "native_stack_top_min_addr"))) { + aot_set_last_error("llvm build in bounds gep failed"); + return false; + } + + return true; +} + static bool create_aux_stack_info(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) { @@ -434,7 +449,8 @@ create_local_variables(AOTCompData *comp_data, AOTCompContext *comp_ctx, } } - if (comp_ctx->enable_stack_bound_check) { + if (comp_ctx->enable_stack_bound_check + || comp_ctx->enable_stack_estimation) { if (aot_func_type->param_count + func->local_count > 0) { func_ctx->last_alloca = func_ctx->locals[aot_func_type->param_count + func->local_count - 1]; @@ -963,6 +979,10 @@ aot_create_func_context(AOTCompData *comp_data, AOTCompContext *comp_ctx, && !create_native_stack_bound(comp_ctx, func_ctx)) { goto fail; } + if (comp_ctx->enable_stack_estimation + && !create_native_stack_top_min(comp_ctx, func_ctx)) { + goto fail; + } /* Get auxiliary stack info */ if (wasm_func->has_op_set_global_aux_stack @@ -1622,6 +1642,9 @@ aot_create_comp_context(AOTCompData *comp_data, aot_comp_option_t option) if (option->disable_llvm_lto) comp_ctx->disable_llvm_lto = true; + if (option->enable_stack_estimation) + comp_ctx->enable_stack_estimation = true; + comp_ctx->opt_level = option->opt_level; comp_ctx->size_level = option->size_level; diff --git a/core/iwasm/compilation/aot_llvm.h b/core/iwasm/compilation/aot_llvm.h index 219f36e73..b982e8083 100644 --- a/core/iwasm/compilation/aot_llvm.h +++ b/core/iwasm/compilation/aot_llvm.h @@ -163,6 +163,7 @@ typedef struct AOTFuncContext { LLVMValueRef aot_inst; LLVMValueRef argv_buf; LLVMValueRef native_stack_bound; + LLVMValueRef native_stack_top_min_addr; LLVMValueRef aux_stack_bound; LLVMValueRef aux_stack_bottom; LLVMValueRef native_symbol; @@ -313,6 +314,9 @@ typedef struct AOTCompContext { /* Native stack bounday Check */ bool enable_stack_bound_check; + /* Native stack usage estimation */ + bool enable_stack_estimation; + /* 128-bit SIMD */ bool enable_simd; @@ -403,6 +407,7 @@ typedef struct AOTCompOption { bool enable_aux_stack_frame; bool disable_llvm_intrinsics; bool disable_llvm_lto; + bool enable_stack_estimation; uint32 opt_level; uint32 size_level; uint32 output_format; diff --git a/core/iwasm/fast-jit/fe/jit_emit_memory.c b/core/iwasm/fast-jit/fe/jit_emit_memory.c index 568766da3..b71d98ba1 100644 --- a/core/iwasm/fast-jit/fe/jit_emit_memory.c +++ b/core/iwasm/fast-jit/fe/jit_emit_memory.c @@ -135,7 +135,8 @@ check_and_seek(JitCompContext *cc, JitReg addr, uint32 offset, uint32 bytes) #ifndef OS_ENABLE_HW_BOUND_CHECK /* ---------- check ---------- */ /* 1. shortcut if the memory size is 0 */ - if (0 == cc->cur_wasm_module->memories[mem_idx].init_page_count) { + if (cc->cur_wasm_module->memories != NULL + && 0 == cc->cur_wasm_module->memories[mem_idx].init_page_count) { JitReg module_inst, cur_page_count; uint32 cur_page_count_offset = (uint32)offsetof(WASMModuleInstance, global_table_data.bytes) @@ -176,6 +177,18 @@ fail: return 0; } +#define CHECK_ALIGNMENT(maddr, memory_data, offset1) \ + do { \ + GEN_INSN(ADD, maddr, memory_data, offset1); \ + JitReg align_mask = NEW_CONST(I64, ((uint64)1 << align) - 1); \ + JitReg AND_res = jit_cc_new_reg_I64(cc); \ + GEN_INSN(AND, AND_res, maddr, align_mask); \ + GEN_INSN(CMP, cc->cmp_reg, AND_res, NEW_CONST(I64, 0)); \ + if (!jit_emit_exception(cc, EXCE_UNALIGNED_ATOMIC, JIT_OP_BNE, \ + cc->cmp_reg, NULL)) \ + goto fail; \ + } while (0) + bool jit_compile_op_i32_load(JitCompContext *cc, uint32 align, uint32 offset, uint32 bytes, bool sign, bool atomic) @@ -779,6 +792,51 @@ bool jit_compile_op_atomic_wait(JitCompContext *cc, uint8 op_type, uint32 align, uint32 offset, uint32 bytes) { + bh_assert(op_type == VALUE_TYPE_I32 || op_type == VALUE_TYPE_I64); + + // Pop atomic.wait arguments + JitReg timeout, expect, expect_64, addr; + POP_I64(timeout); + if (op_type == VALUE_TYPE_I32) { + POP_I32(expect); + expect_64 = jit_cc_new_reg_I64(cc); + GEN_INSN(I32TOI64, expect_64, expect); + } + else { + POP_I64(expect_64); + } + POP_I32(addr); + + // Get referenced address and store it in `maddr` + JitReg memory_data = get_memory_data_reg(cc->jit_frame, 0); + JitReg offset1 = check_and_seek(cc, addr, offset, bytes); + if (!offset1) + goto fail; + JitReg maddr = jit_cc_new_reg_I64(cc); + CHECK_ALIGNMENT(maddr, memory_data, offset1); + + // Prepare `wasm_runtime_atomic_wait` arguments + JitReg res = jit_cc_new_reg_I32(cc); + JitReg args[5] = { 0 }; + args[0] = get_module_inst_reg(cc->jit_frame); + args[1] = maddr; + args[2] = expect_64; + args[3] = timeout; + args[4] = NEW_CONST(I32, false); + + if (!jit_emit_callnative(cc, wasm_runtime_atomic_wait, res, args, + sizeof(args) / sizeof(args[0]))) + goto fail; + + // Handle return code + GEN_INSN(CMP, cc->cmp_reg, res, NEW_CONST(I32, -1)); + if (!jit_emit_exception(cc, EXCE_ALREADY_THROWN, JIT_OP_BEQ, cc->cmp_reg, + NULL)) + goto fail; + + PUSH_I32(res); + return true; +fail: return false; } @@ -786,6 +844,39 @@ bool jit_compiler_op_atomic_notify(JitCompContext *cc, uint32 align, uint32 offset, uint32 bytes) { + // Pop atomic.notify arguments + JitReg notify_count, addr; + POP_I32(notify_count); + POP_I32(addr); + + // Get referenced address and store it in `maddr` + JitReg memory_data = get_memory_data_reg(cc->jit_frame, 0); + JitReg offset1 = check_and_seek(cc, addr, offset, bytes); + if (!offset1) + goto fail; + JitReg maddr = jit_cc_new_reg_I64(cc); + CHECK_ALIGNMENT(maddr, memory_data, offset1); + + // Prepare `wasm_runtime_atomic_notify` arguments + JitReg res = jit_cc_new_reg_I32(cc); + JitReg args[3] = { 0 }; + args[0] = get_module_inst_reg(cc->jit_frame); + args[1] = maddr; + args[2] = notify_count; + + if (!jit_emit_callnative(cc, wasm_runtime_atomic_notify, res, args, + sizeof(args) / sizeof(args[0]))) + goto fail; + + // Handle return code + GEN_INSN(CMP, cc->cmp_reg, res, NEW_CONST(I32, 0)); + if (!jit_emit_exception(cc, EXCE_ALREADY_THROWN, JIT_OP_BLTS, cc->cmp_reg, + NULL)) + goto fail; + + PUSH_I32(res); + return true; +fail: return false; } #endif diff --git a/core/iwasm/fast-jit/jit_codecache.c b/core/iwasm/fast-jit/jit_codecache.c index 66c2d033a..73a034f34 100644 --- a/core/iwasm/fast-jit/jit_codecache.c +++ b/core/iwasm/fast-jit/jit_codecache.c @@ -56,9 +56,31 @@ jit_code_cache_free(void *ptr) bool jit_pass_register_jitted_code(JitCompContext *cc) { - uint32 jit_func_idx = - cc->cur_wasm_func_idx - cc->cur_wasm_module->import_function_count; - cc->cur_wasm_module->fast_jit_func_ptrs[jit_func_idx] = - cc->cur_wasm_func->fast_jit_jitted_code = cc->jitted_addr_begin; + WASMModuleInstance *instance; + WASMModule *module = cc->cur_wasm_module; + WASMFunction *func = cc->cur_wasm_func; + uint32 jit_func_idx = cc->cur_wasm_func_idx - module->import_function_count; + +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ + && WASM_ENABLE_LAZY_JIT != 0 + os_mutex_lock(&module->instance_list_lock); +#endif + + module->fast_jit_func_ptrs[jit_func_idx] = func->fast_jit_jitted_code = + cc->jitted_addr_begin; + +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ + && WASM_ENABLE_LAZY_JIT != 0 + instance = module->instance_list; + while (instance) { + if (instance->e->running_mode == Mode_Fast_JIT) + instance->fast_jit_func_ptrs[jit_func_idx] = cc->jitted_addr_begin; + instance = instance->e->next; + } + + os_mutex_unlock(&module->instance_list_lock); +#else + (void)instance; +#endif return true; } diff --git a/core/iwasm/fast-jit/jit_compiler.c b/core/iwasm/fast-jit/jit_compiler.c index 67dcb7b51..958d0e987 100644 --- a/core/iwasm/fast-jit/jit_compiler.c +++ b/core/iwasm/fast-jit/jit_compiler.c @@ -157,8 +157,16 @@ jit_compiler_compile(WASMModule *module, uint32 func_idx) /* Apply compiler passes */ if (!apply_compiler_passes(cc) || jit_get_last_error(cc)) { last_error = jit_get_last_error(cc); + +#if WASM_ENABLE_CUSTOM_NAME_SECTION != 0 + char *function_name = cc->cur_wasm_func->field_name; + os_printf("fast jit compilation failed: %s (function_name=%s)\n", + last_error ? last_error : "unknown error", function_name); +#else os_printf("fast jit compilation failed: %s\n", last_error ? last_error : "unknown error"); +#endif + goto fail; } @@ -246,6 +254,8 @@ jit_compiler_set_call_to_fast_jit(WASMModule *module, uint32 func_idx) func_ptr = jit_codegen_compile_call_to_fast_jit(module, func_idx); if (func_ptr) { + uint32 i = func_idx - module->import_function_count; + module->functions[i]->call_to_fast_jit_from_llvm_jit = func_ptr; jit_compiler_set_llvm_jit_func_ptr(module, func_idx, func_ptr); } @@ -259,12 +269,14 @@ jit_compiler_set_llvm_jit_func_ptr(WASMModule *module, uint32 func_idx, WASMModuleInstance *instance; uint32 i = func_idx - module->import_function_count; - module->functions[i]->llvm_jit_func_ptr = module->func_ptrs[i] = func_ptr; - os_mutex_lock(&module->instance_list_lock); + + module->func_ptrs[i] = func_ptr; + instance = module->instance_list; while (instance) { - instance->func_ptrs[func_idx] = func_ptr; + if (instance->e->running_mode == Mode_Multi_Tier_JIT) + instance->func_ptrs[func_idx] = func_ptr; instance = instance->e->next; } os_mutex_unlock(&module->instance_list_lock); diff --git a/core/iwasm/fast-jit/jit_frontend.c b/core/iwasm/fast-jit/jit_frontend.c index b20be9609..5ca829645 100644 --- a/core/iwasm/fast-jit/jit_frontend.c +++ b/core/iwasm/fast-jit/jit_frontend.c @@ -841,7 +841,7 @@ init_func_translation(JitCompContext *cc) cc->spill_cache_offset = wasm_interp_interp_frame_size(total_cell_num); /* Set spill cache size according to max local cell num, max stack cell num and virtual fixed register num */ - cc->spill_cache_size = (max_locals + max_stacks) * 4 + sizeof(void *) * 5; + cc->spill_cache_size = (max_locals + max_stacks) * 4 + sizeof(void *) * 16; cc->total_frame_size = cc->spill_cache_offset + cc->spill_cache_size; cc->jitted_return_address_offset = offsetof(WASMInterpFrame, jitted_return_addr); diff --git a/core/iwasm/fast-jit/jit_ir.c b/core/iwasm/fast-jit/jit_ir.c index 9fc8d5ccb..db0d86ca5 100644 --- a/core/iwasm/fast-jit/jit_ir.c +++ b/core/iwasm/fast-jit/jit_ir.c @@ -560,6 +560,7 @@ address_of_const(JitCompContext *cc, JitReg reg, unsigned size) unsigned no = jit_reg_no(reg); unsigned idx = no & ~_JIT_REG_CONST_IDX_FLAG; + bh_assert(kind < JIT_REG_KIND_L32); bh_assert(jit_reg_is_const_idx(reg) && idx < cc->_const_val._num[kind]); return cc->_const_val._value[kind] + size * idx; @@ -572,6 +573,7 @@ next_of_const(JitCompContext *cc, JitReg reg) unsigned no = jit_reg_no(reg); unsigned idx = no & ~_JIT_REG_CONST_IDX_FLAG; + bh_assert(kind < JIT_REG_KIND_L32); bh_assert(jit_reg_is_const_idx(reg) && idx < cc->_const_val._num[kind]); return cc->_const_val._next[kind][idx]; diff --git a/core/iwasm/fast-jit/jit_ir.h b/core/iwasm/fast-jit/jit_ir.h index 9f91d8a47..632e8ed18 100644 --- a/core/iwasm/fast-jit/jit_ir.h +++ b/core/iwasm/fast-jit/jit_ir.h @@ -1688,6 +1688,7 @@ jit_cc_is_hreg(JitCompContext *cc, JitReg reg) unsigned kind = jit_reg_kind(reg); unsigned no = jit_reg_no(reg); bh_assert(jit_reg_is_variable(reg)); + bh_assert(kind < JIT_REG_KIND_L32); return no < cc->hreg_info->info[kind].num; } @@ -1705,6 +1706,7 @@ jit_cc_is_hreg_fixed(JitCompContext *cc, JitReg reg) unsigned kind = jit_reg_kind(reg); unsigned no = jit_reg_no(reg); bh_assert(jit_cc_is_hreg(cc, reg)); + bh_assert(kind < JIT_REG_KIND_L32); return !!cc->hreg_info->info[kind].fixed[no]; } @@ -1722,6 +1724,7 @@ jit_cc_is_hreg_caller_saved_native(JitCompContext *cc, JitReg reg) unsigned kind = jit_reg_kind(reg); unsigned no = jit_reg_no(reg); bh_assert(jit_cc_is_hreg(cc, reg)); + bh_assert(kind < JIT_REG_KIND_L32); return !!cc->hreg_info->info[kind].caller_saved_native[no]; } @@ -1739,6 +1742,7 @@ jit_cc_is_hreg_caller_saved_jitted(JitCompContext *cc, JitReg reg) unsigned kind = jit_reg_kind(reg); unsigned no = jit_reg_no(reg); bh_assert(jit_cc_is_hreg(cc, reg)); + bh_assert(kind < JIT_REG_KIND_L32); return !!cc->hreg_info->info[kind].caller_saved_jitted[no]; } diff --git a/core/iwasm/fast-jit/jit_regalloc.c b/core/iwasm/fast-jit/jit_regalloc.c index 415b9e4b8..5bff465fb 100644 --- a/core/iwasm/fast-jit/jit_regalloc.c +++ b/core/iwasm/fast-jit/jit_regalloc.c @@ -156,6 +156,7 @@ rc_get_vr(RegallocContext *rc, JitReg vreg) unsigned no = jit_reg_no(vreg); bh_assert(jit_reg_is_variable(vreg)); + bh_assert(kind < JIT_REG_KIND_L32); return &rc->vregs[kind][no]; } @@ -175,6 +176,7 @@ rc_get_hr(RegallocContext *rc, JitReg hreg) unsigned no = jit_reg_no(hreg); bh_assert(jit_reg_is_variable(hreg) && jit_cc_is_hreg(rc->cc, hreg)); + bh_assert(kind < JIT_REG_KIND_L32); return &rc->hregs[kind][no]; } @@ -208,7 +210,9 @@ static unsigned get_reg_stride(JitReg reg) { static const uint8 strides[] = { 0, 1, 2, 1, 2, 2, 4, 8, 0 }; - return strides[jit_reg_kind(reg)]; + uint32 kind = jit_reg_kind(reg); + bh_assert(kind <= JIT_REG_KIND_L32); + return strides[kind]; } /** @@ -582,13 +586,17 @@ static JitReg allocate_hreg(RegallocContext *rc, JitReg vreg, JitInsn *insn, int distance) { const int kind = jit_reg_kind(vreg); - const HardReg *hregs = rc->hregs[kind]; - const unsigned hreg_num = jit_cc_hreg_num(rc->cc, kind); + const HardReg *hregs; + unsigned hreg_num; JitReg hreg, vreg_to_reload = 0; int min_distance = distance, vr_distance; VirtualReg *vr = rc_get_vr(rc, vreg); unsigned i; + bh_assert(kind < JIT_REG_KIND_L32); + hregs = rc->hregs[kind]; + hreg_num = jit_cc_hreg_num(rc->cc, kind); + if (hreg_num == 0) /* Unsupported hard register kind. */ { diff --git a/core/iwasm/include/aot_export.h b/core/iwasm/include/aot_export.h index f8df168e8..792a4baa9 100644 --- a/core/iwasm/include/aot_export.h +++ b/core/iwasm/include/aot_export.h @@ -55,6 +55,7 @@ typedef struct AOTCompOption { bool enable_aux_stack_frame; bool disable_llvm_intrinsics; bool disable_llvm_lto; + bool enable_stack_estimation; uint32_t opt_level; uint32_t size_level; uint32_t output_format; diff --git a/core/iwasm/include/wasm_c_api.h b/core/iwasm/include/wasm_c_api.h index 85fcd1868..324a43bd5 100644 --- a/core/iwasm/include/wasm_c_api.h +++ b/core/iwasm/include/wasm_c_api.h @@ -354,6 +354,7 @@ WASM_API_EXTERN own wasm_importtype_t* wasm_importtype_new( WASM_API_EXTERN const wasm_name_t* wasm_importtype_module(const wasm_importtype_t*); WASM_API_EXTERN const wasm_name_t* wasm_importtype_name(const wasm_importtype_t*); WASM_API_EXTERN const wasm_externtype_t* wasm_importtype_type(const wasm_importtype_t*); +WASM_API_EXTERN bool wasm_importtype_is_linked(const wasm_importtype_t*); // Export Types @@ -797,6 +798,9 @@ static inline void* wasm_val_ptr(const wasm_val_t* val) { #define KILOBYTE(n) ((n) * 1024) +// Create placeholders filled in `wasm_externvec_t* imports` for `wasm_instance_new()` +WASM_API_EXTERN wasm_extern_t *wasm_extern_new_empty(wasm_store_t *, wasm_externkind_t); + /////////////////////////////////////////////////////////////////////////////// #undef own diff --git a/core/iwasm/include/wasm_export.h b/core/iwasm/include/wasm_export.h index 2d5e85155..7f82b6de5 100644 --- a/core/iwasm/include/wasm_export.h +++ b/core/iwasm/include/wasm_export.h @@ -131,6 +131,14 @@ typedef struct mem_alloc_info_t { uint32_t highmark_size; } mem_alloc_info_t; +/* Running mode of runtime and module instance*/ +typedef enum RunningMode { + Mode_Interp = 1, + Mode_Fast_JIT, + Mode_LLVM_JIT, + Mode_Multi_Tier_JIT, +} RunningMode; + /* WASM runtime initialize arguments */ typedef struct RuntimeInitArgs { mem_alloc_type_t mem_alloc_type; @@ -152,6 +160,13 @@ typedef struct RuntimeInitArgs { /* Fast JIT code cache size */ uint32_t fast_jit_code_cache_size; + + /* Default running mode of the runtime */ + RunningMode running_mode; + + /* LLVM JIT opt and size level */ + uint32_t llvm_jit_opt_level; + uint32_t llvm_jit_size_level; } RuntimeInitArgs; #ifndef WASM_VALKIND_T_DEFINED @@ -195,9 +210,9 @@ WASM_RUNTIME_API_EXTERN bool wasm_runtime_init(void); /** - * Initialize the WASM runtime environment, and also initialize - * the memory allocator and register native symbols, which are specified - * with init arguments + * Initialize the WASM runtime environment, WASM running mode, + * and also initialize the memory allocator and register native symbols, + * which are specified with init arguments * * @param init_args specifies the init arguments * @@ -206,6 +221,28 @@ wasm_runtime_init(void); WASM_RUNTIME_API_EXTERN bool wasm_runtime_full_init(RuntimeInitArgs *init_args); +/** + * Query whether a certain running mode is supported for the runtime + * + * @param running_mode the running mode to query + * + * @return true if this running mode is supported, false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_is_running_mode_supported(RunningMode running_mode); + +/** + * Set the default running mode for the runtime. It is inherited + * to set the running mode of a module instance when it is instantiated, + * and can be changed by calling wasm_runtime_set_running_mode + * + * @param running_mode the running mode to set + * + * @return true if success, false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_set_default_running_mode(RunningMode running_mode); + /** * Destroy the WASM runtime environment. */ @@ -450,6 +487,34 @@ wasm_runtime_instantiate(const wasm_module_t module, uint32_t stack_size, uint32_t heap_size, char *error_buf, uint32_t error_buf_size); +/** + * Set the running mode of a WASM module instance, override the + * default running mode of the runtime. Note that it only makes sense when + * the input is a wasm bytecode file: for the AOT file, runtime always runs + * it with AOT engine, and this function always returns true. + * + * @param module_inst the WASM module instance to set running mode + * @param running_mode the running mode to set + * + * @return true if success, false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_set_running_mode(wasm_module_inst_t module_inst, + RunningMode running_mode); + +/** + * Get the running mode of a WASM module instance, if no running mode + * is explicitly set the default running mode of runtime will + * be used and returned. Note that it only makes sense when the input is a + * wasm bytecode file: for the AOT file, this function always returns 0. + * + * @param module_inst the WASM module instance to query for running mode + * + * @return the running mode this module instance currently use + */ +WASM_RUNTIME_API_EXTERN RunningMode +wasm_runtime_get_running_mode(wasm_module_inst_t module_inst); + /** * Deinstantiate a WASM module instance, destroy the resources. * @@ -735,6 +800,31 @@ wasm_runtime_call_wasm_v(wasm_exec_env_t exec_env, uint32_t num_results, wasm_val_t results[], uint32_t num_args, ...); +/** + * Call a function reference of a given WASM runtime instance with + * arguments. + * + * Note: this can be used to call a function which is not exported + * by the module explicitly. You might consider it as an abstraction + * violation. + * + * @param exec_env the execution environment to call the function + * which must be created from wasm_create_exec_env() + * @param element_index the function reference index, usually + * prvovided by the caller of a registed native function + * @param argc the number of arguments + * @param argv the arguments. If the function method has return value, + * the first (or first two in case 64-bit return value) element of + * argv stores the return value of the called WASM function after this + * function returns. + * + * @return true if success, false otherwise and exception will be thrown, + * the caller can call wasm_runtime_get_exception to get exception info. + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_call_indirect(wasm_exec_env_t exec_env, uint32_t element_index, + uint32_t argc, uint32_t argv[]); + /** * Find the unique main function from a WASM module instance * and execute that function. @@ -1259,6 +1349,22 @@ wasm_runtime_get_custom_section(wasm_module_t const module_comm, */ WASM_RUNTIME_API_EXTERN void wasm_runtime_get_version(uint32_t *major, uint32_t *minor, uint32_t *patch); + +/** + * Check whether an import func `(import (func ...))` is linked or not + * with runtime registered natvie functions + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_is_import_func_linked(const char *module_name, + const char *func_name); + +/** + * Check whether an import global `(import (global ...))` is linked or not + * with runtime registered natvie globals + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_is_import_global_linked(const char *module_name, + const char *global_name); /* clang-format on */ #ifdef __cplusplus diff --git a/core/iwasm/interpreter/wasm.h b/core/iwasm/interpreter/wasm.h index 7efa6d5ff..e6d02c3f1 100644 --- a/core/iwasm/interpreter/wasm.h +++ b/core/iwasm/interpreter/wasm.h @@ -278,9 +278,14 @@ struct WASMFunction { #endif #if WASM_ENABLE_FAST_JIT != 0 + /* The compiled fast jit jitted code block of this function */ void *fast_jit_jitted_code; #if WASM_ENABLE_JIT != 0 && WASM_ENABLE_LAZY_JIT != 0 + /* The compiled llvm jit func ptr of this function */ void *llvm_jit_func_ptr; + /* Code block to call fast jit jitted code of this function + from the llvm jit jitted code */ + void *call_to_fast_jit_from_llvm_jit; #endif #endif }; @@ -505,15 +510,14 @@ struct WASMModule { uint64 load_size; #endif -#if WASM_ENABLE_DEBUG_INTERP != 0 \ - || (WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT \ +#if WASM_ENABLE_DEBUG_INTERP != 0 \ + || (WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ && WASM_ENABLE_LAZY_JIT != 0) /** * List of instances referred to this module. When source debugging * feature is enabled, the debugger may modify the code section of * the module, so we need to report a warning if user create several - * instances based on the same module. Sub instances created by - * lib-pthread or spawn API won't be added into the list. + * instances based on the same module. * * Also add the instance to the list for Fast JIT to LLVM JIT * tier-up, since we need to lazily update the LLVM func pointers @@ -533,7 +537,22 @@ struct WASMModule { #endif #if WASM_ENABLE_FAST_JIT != 0 - /* func pointers of Fast JITed (un-imported) functions */ + /** + * func pointers of Fast JITed (un-imported) functions + * for non Multi-Tier JIT mode: + * (1) when lazy jit is disabled, each pointer is set to the compiled + * fast jit jitted code + * (2) when lazy jit is enabled, each pointer is firstly inited as + * jit_global->compile_fast_jit_and_then_call, and then set to the + * compiled fast jit jitted code when it is called (the stub will + * compile the jit function and then update itself) + * for Multi-Tier JIT mode: + * each pointer is firstly inited as compile_fast_jit_and_then_call, + * and then set to the compiled fast jit jitted code when it is called, + * and when the llvm jit func ptr of the same function is compiled, it + * will be set to call_to_llvm_jit_from_fast_jit of this function type + * (tier-up from fast-jit to llvm-jit) + */ void **fast_jit_func_ptrs; /* locks for Fast JIT lazy compilation */ korp_mutex fast_jit_thread_locks[WASM_ORC_JIT_BACKEND_THREAD_NUM]; @@ -543,7 +562,16 @@ struct WASMModule { #if WASM_ENABLE_JIT != 0 struct AOTCompData *comp_data; struct AOTCompContext *comp_ctx; - /* func pointers of LLVM JITed (un-imported) functions */ + /** + * func pointers of LLVM JITed (un-imported) functions + * for non Multi-Tier JIT mode: + * each pointer is set to the lookuped llvm jit func ptr, note that it + * is a stub and will trigger the actual compilation when it is called + * for Multi-Tier JIT mode: + * each pointer is inited as call_to_fast_jit code block, when the llvm + * jit func ptr is actually compiled, it is set to the compiled llvm jit + * func ptr + */ void **func_ptrs; /* whether the func pointers are compiled */ bool *func_ptrs_compiled; @@ -568,6 +596,12 @@ struct WASMModule { korp_tid llvm_jit_init_thread; /* whether the llvm jit is initialized */ bool llvm_jit_inited; + /* Whether to enable llvm jit compilation: + it is set to true only when there is a module instance starts to + run with running mode Mode_LLVM_JIT or Mode_Multi_Tier_JIT, + since no need to enable llvm jit compilation for Mode_Interp and + Mode_Fast_JIT, so as to improve performance for them */ + bool enable_llvm_jit_compilation; #endif }; diff --git a/core/iwasm/interpreter/wasm_interp_classic.c b/core/iwasm/interpreter/wasm_interp_classic.c index 63edbbf51..03c3e5591 100644 --- a/core/iwasm/interpreter/wasm_interp_classic.c +++ b/core/iwasm/interpreter/wasm_interp_classic.c @@ -8,6 +8,7 @@ #include "wasm_runtime.h" #include "wasm_opcode.h" #include "wasm_loader.h" +#include "wasm_memory.h" #include "../common/wasm_exec_env.h" #if WASM_ENABLE_SHARED_MEMORY != 0 #include "../common/wasm_shared_memory.h" @@ -696,28 +697,28 @@ trunc_f64_to_int(WASMModuleInstance *module, uint32 *frame_sp, float64 src_min, CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 1, maddr); \ CHECK_ATOMIC_MEMORY_ACCESS(); \ \ - os_mutex_lock(&module->e->mem_lock); \ + os_mutex_lock(&node->shared_mem_lock); \ readv = (uint32)(*(uint8 *)maddr); \ *(uint8 *)maddr = (uint8)(readv op sval); \ - os_mutex_unlock(&module->e->mem_lock); \ + os_mutex_unlock(&node->shared_mem_lock); \ } \ else if (opcode == WASM_OP_ATOMIC_RMW_I32_##OP_NAME##16_U) { \ CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 2, maddr); \ CHECK_ATOMIC_MEMORY_ACCESS(); \ \ - os_mutex_lock(&module->e->mem_lock); \ + os_mutex_lock(&node->shared_mem_lock); \ readv = (uint32)LOAD_U16(maddr); \ STORE_U16(maddr, (uint16)(readv op sval)); \ - os_mutex_unlock(&module->e->mem_lock); \ + os_mutex_unlock(&node->shared_mem_lock); \ } \ else { \ CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 4, maddr); \ CHECK_ATOMIC_MEMORY_ACCESS(); \ \ - os_mutex_lock(&module->e->mem_lock); \ + os_mutex_lock(&node->shared_mem_lock); \ readv = LOAD_I32(maddr); \ STORE_U32(maddr, readv op sval); \ - os_mutex_unlock(&module->e->mem_lock); \ + os_mutex_unlock(&node->shared_mem_lock); \ } \ PUSH_I32(readv); \ break; \ @@ -736,39 +737,39 @@ trunc_f64_to_int(WASMModuleInstance *module, uint32 *frame_sp, float64 src_min, CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 1, maddr); \ CHECK_ATOMIC_MEMORY_ACCESS(); \ \ - os_mutex_lock(&module->e->mem_lock); \ + os_mutex_lock(&node->shared_mem_lock); \ readv = (uint64)(*(uint8 *)maddr); \ *(uint8 *)maddr = (uint8)(readv op sval); \ - os_mutex_unlock(&module->e->mem_lock); \ + os_mutex_unlock(&node->shared_mem_lock); \ } \ else if (opcode == WASM_OP_ATOMIC_RMW_I64_##OP_NAME##16_U) { \ CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 2, maddr); \ CHECK_ATOMIC_MEMORY_ACCESS(); \ \ - os_mutex_lock(&module->e->mem_lock); \ + os_mutex_lock(&node->shared_mem_lock); \ readv = (uint64)LOAD_U16(maddr); \ STORE_U16(maddr, (uint16)(readv op sval)); \ - os_mutex_unlock(&module->e->mem_lock); \ + os_mutex_unlock(&node->shared_mem_lock); \ } \ else if (opcode == WASM_OP_ATOMIC_RMW_I64_##OP_NAME##32_U) { \ CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 4, maddr); \ CHECK_ATOMIC_MEMORY_ACCESS(); \ \ - os_mutex_lock(&module->e->mem_lock); \ + os_mutex_lock(&node->shared_mem_lock); \ readv = (uint64)LOAD_U32(maddr); \ STORE_U32(maddr, (uint32)(readv op sval)); \ - os_mutex_unlock(&module->e->mem_lock); \ + os_mutex_unlock(&node->shared_mem_lock); \ } \ else { \ uint64 op_result; \ CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 8, maddr); \ CHECK_ATOMIC_MEMORY_ACCESS(); \ \ - os_mutex_lock(&module->e->mem_lock); \ + os_mutex_lock(&node->shared_mem_lock); \ readv = (uint64)LOAD_I64(maddr); \ op_result = readv op sval; \ STORE_I64(maddr, op_result); \ - os_mutex_unlock(&module->e->mem_lock); \ + os_mutex_unlock(&node->shared_mem_lock); \ } \ PUSH_I64(readv); \ break; \ @@ -891,7 +892,7 @@ wasm_interp_call_func_native(WASMModuleInstance *module_inst, if (!func_import->call_conv_wasm_c_api) { native_func_pointer = module_inst->import_func_ptrs[cur_func_index]; } - else { + else if (module_inst->e->c_api_func_imports) { c_api_func_import = module_inst->e->c_api_func_imports + cur_func_index; native_func_pointer = c_api_func_import->func_ptr_linked; } @@ -954,7 +955,7 @@ fast_jit_invoke_native(WASMExecEnv *exec_env, uint32 func_idx, WASMFunctionInstance *cur_func = module_inst->e->functions + func_idx; wasm_interp_call_func_native(module_inst, exec_env, cur_func, prev_frame); - return wasm_get_exception(module_inst) ? false : true; + return wasm_copy_exception(module_inst, NULL) ? false : true; } #endif @@ -1023,7 +1024,7 @@ wasm_interp_call_func_import(WASMModuleInstance *module_inst, exec_env->module_inst = (WASMModuleInstanceCommon *)module_inst; /* transfer exception if it is thrown */ - if (wasm_get_exception(sub_module_inst)) { + if (wasm_copy_exception(sub_module_inst, NULL)) { bh_memcpy_s(module_inst->cur_exception, sizeof(module_inst->cur_exception), sub_module_inst->cur_exception, @@ -1036,21 +1037,25 @@ wasm_interp_call_func_import(WASMModuleInstance *module_inst, #if WASM_ENABLE_DEBUG_INTERP != 0 #define CHECK_SUSPEND_FLAGS() \ do { \ + os_mutex_lock(&exec_env->wait_lock); \ if (IS_WAMR_TERM_SIG(exec_env->current_status->signal_flag)) { \ + os_mutex_unlock(&exec_env->wait_lock); \ return; \ } \ if (IS_WAMR_STOP_SIG(exec_env->current_status->signal_flag)) { \ SYNC_ALL_TO_FRAME(); \ - wasm_cluster_thread_stopped(exec_env); \ wasm_cluster_thread_waiting_run(exec_env); \ } \ + os_mutex_unlock(&exec_env->wait_lock); \ } while (0) #else #define CHECK_SUSPEND_FLAGS() \ do { \ + os_mutex_lock(&exec_env->wait_lock); \ if (exec_env->suspend_flags.flags != 0) { \ if (exec_env->suspend_flags.flags & 0x01) { \ /* terminate current thread */ \ + os_mutex_unlock(&exec_env->wait_lock); \ return; \ } \ while (exec_env->suspend_flags.flags & 0x02) { \ @@ -1058,6 +1063,7 @@ wasm_interp_call_func_import(WASMModuleInstance *module_inst, os_cond_wait(&exec_env->wait_cond, &exec_env->wait_lock); \ } \ } \ + os_mutex_unlock(&exec_env->wait_lock); \ } while (0) #endif /* WASM_ENABLE_DEBUG_INTERP */ #endif /* WASM_ENABLE_THREAD_MGR */ @@ -1077,7 +1083,6 @@ wasm_interp_call_func_import(WASMModuleInstance *module_inst, && exec_env->current_status->step_count++ == 1) { \ exec_env->current_status->step_count = 0; \ SYNC_ALL_TO_FRAME(); \ - wasm_cluster_thread_stopped(exec_env); \ wasm_cluster_thread_waiting_run(exec_env); \ } \ goto *handle_table[*frame_ip++]; \ @@ -1094,7 +1099,6 @@ wasm_interp_call_func_import(WASMModuleInstance *module_inst, && exec_env->current_status->step_count++ == 2) { \ exec_env->current_status->step_count = 0; \ SYNC_ALL_TO_FRAME(); \ - wasm_cluster_thread_stopped(exec_env); \ wasm_cluster_thread_waiting_run(exec_env); \ } \ continue @@ -1123,14 +1127,22 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, WASMFunctionInstance *cur_func, WASMInterpFrame *prev_frame) { +#if WASM_ENABLE_SHARED_MEMORY != 0 + WASMSharedMemNode *node = + wasm_module_get_shared_memory((WASMModuleCommon *)module->module); +#else + void *node = NULL; +#endif + WASMMemoryInstance *memory = wasm_get_default_memory(module); uint8 *global_data = module->global_data; #if !defined(OS_ENABLE_HW_BOUND_CHECK) \ || WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 \ || WASM_ENABLE_BULK_MEMORY != 0 - uint32 num_bytes_per_page = memory ? memory->num_bytes_per_page : 0; + uint32 num_bytes_per_page = + memory ? wasm_get_num_bytes_per_page(memory, node) : 0; uint32 linear_mem_size = - memory ? num_bytes_per_page * memory->cur_page_count : 0; + memory ? wasm_get_linear_memory_size(memory, node) : 0; #endif WASMType **wasm_types = module->module->types; WASMGlobalInstance *globals = module->e->globals, *global; @@ -1365,7 +1377,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, HANDLE_OP(EXT_OP_BR_TABLE_CACHE) { - BrTableCache *node = + BrTableCache *node_cache = bh_list_first_elem(module->module->br_table_cache_list); BrTableCache *node_next; @@ -1374,13 +1386,13 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, #endif lidx = POP_I32(); - while (node) { - node_next = bh_list_elem_next(node); - if (node->br_table_op_addr == frame_ip - 1) { - depth = node->br_depths[lidx]; + while (node_cache) { + node_next = bh_list_elem_next(node_cache); + if (node_cache->br_table_op_addr == frame_ip - 1) { + depth = node_cache->br_depths[lidx]; goto label_pop_csp_n; } - node = node_next; + node_cache = node_next; } bh_assert(0); HANDLE_OP_END(); @@ -3434,6 +3446,10 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, if (ret == (uint32)-1) goto got_exception; +#if WASM_ENABLE_THREAD_MGR != 0 + CHECK_SUSPEND_FLAGS(); +#endif + PUSH_I32(ret); break; } @@ -3454,6 +3470,10 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, if (ret == (uint32)-1) goto got_exception; +#if WASM_ENABLE_THREAD_MGR != 0 + CHECK_SUSPEND_FLAGS(); +#endif + PUSH_I32(ret); break; } @@ -3461,6 +3481,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, { /* Skip the memory index */ frame_ip++; + os_atomic_thread_fence(os_memory_order_release); break; } @@ -3475,23 +3496,23 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, if (opcode == WASM_OP_ATOMIC_I32_LOAD8_U) { CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 1, maddr); CHECK_ATOMIC_MEMORY_ACCESS(); - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); readv = (uint32)(*(uint8 *)maddr); - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } else if (opcode == WASM_OP_ATOMIC_I32_LOAD16_U) { CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 2, maddr); CHECK_ATOMIC_MEMORY_ACCESS(); - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); readv = (uint32)LOAD_U16(maddr); - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } else { CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 4, maddr); CHECK_ATOMIC_MEMORY_ACCESS(); - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); readv = LOAD_I32(maddr); - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } PUSH_I32(readv); @@ -3510,30 +3531,30 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, if (opcode == WASM_OP_ATOMIC_I64_LOAD8_U) { CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 1, maddr); CHECK_ATOMIC_MEMORY_ACCESS(); - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); readv = (uint64)(*(uint8 *)maddr); - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } else if (opcode == WASM_OP_ATOMIC_I64_LOAD16_U) { CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 2, maddr); CHECK_ATOMIC_MEMORY_ACCESS(); - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); readv = (uint64)LOAD_U16(maddr); - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } else if (opcode == WASM_OP_ATOMIC_I64_LOAD32_U) { CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 4, maddr); CHECK_ATOMIC_MEMORY_ACCESS(); - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); readv = (uint64)LOAD_U32(maddr); - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } else { CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 8, maddr); CHECK_ATOMIC_MEMORY_ACCESS(); - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); readv = LOAD_I64(maddr); - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } PUSH_I64(readv); @@ -3552,23 +3573,23 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, if (opcode == WASM_OP_ATOMIC_I32_STORE8) { CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 1, maddr); CHECK_ATOMIC_MEMORY_ACCESS(); - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); *(uint8 *)maddr = (uint8)sval; - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } else if (opcode == WASM_OP_ATOMIC_I32_STORE16) { CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 2, maddr); CHECK_ATOMIC_MEMORY_ACCESS(); - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); STORE_U16(maddr, (uint16)sval); - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } else { CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 4, maddr); CHECK_ATOMIC_MEMORY_ACCESS(); - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); STORE_U32(maddr, frame_sp[1]); - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } break; } @@ -3586,31 +3607,31 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, if (opcode == WASM_OP_ATOMIC_I64_STORE8) { CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 1, maddr); CHECK_ATOMIC_MEMORY_ACCESS(); - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); *(uint8 *)maddr = (uint8)sval; - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } else if (opcode == WASM_OP_ATOMIC_I64_STORE16) { CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 2, maddr); CHECK_ATOMIC_MEMORY_ACCESS(); - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); STORE_U16(maddr, (uint16)sval); - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } else if (opcode == WASM_OP_ATOMIC_I64_STORE32) { CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 4, maddr); CHECK_ATOMIC_MEMORY_ACCESS(); - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); STORE_U32(maddr, (uint32)sval); - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } else { CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 8, maddr); CHECK_ATOMIC_MEMORY_ACCESS(); - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); PUT_I64_TO_ADDR((uint32 *)maddr, GET_I64_FROM_ADDR(frame_sp + 1)); - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } break; } @@ -3630,32 +3651,32 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, CHECK_ATOMIC_MEMORY_ACCESS(); expect = (uint8)expect; - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); readv = (uint32)(*(uint8 *)maddr); if (readv == expect) *(uint8 *)maddr = (uint8)(sval); - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } else if (opcode == WASM_OP_ATOMIC_RMW_I32_CMPXCHG16_U) { CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 2, maddr); CHECK_ATOMIC_MEMORY_ACCESS(); expect = (uint16)expect; - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); readv = (uint32)LOAD_U16(maddr); if (readv == expect) STORE_U16(maddr, (uint16)(sval)); - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } else { CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 4, maddr); CHECK_ATOMIC_MEMORY_ACCESS(); - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); readv = LOAD_I32(maddr); if (readv == expect) STORE_U32(maddr, sval); - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } PUSH_I32(readv); break; @@ -3676,44 +3697,44 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, CHECK_ATOMIC_MEMORY_ACCESS(); expect = (uint8)expect; - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); readv = (uint64)(*(uint8 *)maddr); if (readv == expect) *(uint8 *)maddr = (uint8)(sval); - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } else if (opcode == WASM_OP_ATOMIC_RMW_I64_CMPXCHG16_U) { CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 2, maddr); CHECK_ATOMIC_MEMORY_ACCESS(); expect = (uint16)expect; - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); readv = (uint64)LOAD_U16(maddr); if (readv == expect) STORE_U16(maddr, (uint16)(sval)); - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } else if (opcode == WASM_OP_ATOMIC_RMW_I64_CMPXCHG32_U) { CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 4, maddr); CHECK_ATOMIC_MEMORY_ACCESS(); expect = (uint32)expect; - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); readv = (uint64)LOAD_U32(maddr); if (readv == expect) STORE_U32(maddr, (uint32)(sval)); - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } else { CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 8, maddr); CHECK_ATOMIC_MEMORY_ACCESS(); - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); readv = (uint64)LOAD_I64(maddr); if (readv == expect) { STORE_I64(maddr, sval); } - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } PUSH_I64(readv); break; @@ -3859,7 +3880,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, if (memory) linear_mem_size = num_bytes_per_page * memory->cur_page_count; #endif - if (wasm_get_exception(module)) + if (wasm_copy_exception(module, NULL)) goto got_exception; } else { @@ -3908,10 +3929,10 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, PUSH_CSP(LABEL_TYPE_FUNCTION, 0, cell_num, frame_ip_end - 1); wasm_exec_env_set_cur_frame(exec_env, frame); -#if WASM_ENABLE_THREAD_MGR != 0 - CHECK_SUSPEND_FLAGS(); -#endif } +#if WASM_ENABLE_THREAD_MGR != 0 + CHECK_SUSPEND_FLAGS(); +#endif HANDLE_OP_END(); } @@ -4002,7 +4023,8 @@ fast_jit_call_func_bytecode(WASMModuleInstance *module_inst, module_inst->fast_jit_func_ptrs[func_idx_non_import]); bh_assert(action == JIT_INTERP_ACTION_NORMAL || (action == JIT_INTERP_ACTION_THROWN - && wasm_runtime_get_exception(exec_env->module_inst))); + && wasm_copy_exception( + (WASMModuleInstance *)exec_env->module_inst, NULL))); /* Get the return values form info.out.ret */ if (func_type->result_count) { @@ -4034,24 +4056,6 @@ fast_jit_call_func_bytecode(WASMModuleInstance *module_inst, #endif /* end of WASM_ENABLE_FAST_JIT != 0 */ #if WASM_ENABLE_JIT != 0 -static bool -clear_wasi_proc_exit_exception(WASMModuleInstance *module_inst) -{ -#if WASM_ENABLE_LIBC_WASI != 0 - const char *exception = wasm_get_exception(module_inst); - if (exception && !strcmp(exception, "Exception: wasi proc exit")) { - /* The "wasi proc exit" exception is thrown by native lib to - let wasm app exit, which is a normal behavior, we clear - the exception here. */ - wasm_set_exception(module_inst, NULL); - return true; - } - return false; -#else - return false; -#endif -} - static bool llvm_jit_call_func_bytecode(WASMModuleInstance *module_inst, WASMExecEnv *exec_env, @@ -4111,14 +4115,6 @@ llvm_jit_call_func_bytecode(WASMModuleInstance *module_inst, ret = wasm_runtime_invoke_native( exec_env, module_inst->func_ptrs[func_idx], func_type, NULL, NULL, argv1, argc, argv); - - if (!ret || wasm_get_exception(module_inst)) { - if (clear_wasi_proc_exit_exception(module_inst)) - ret = true; - else - ret = false; - } - if (!ret) { if (argv1 != argv1_buf) wasm_runtime_free(argv1); @@ -4163,10 +4159,7 @@ llvm_jit_call_func_bytecode(WASMModuleInstance *module_inst, exec_env, module_inst->func_ptrs[func_idx], func_type, NULL, NULL, argv, argc, argv); - if (clear_wasi_proc_exit_exception(module_inst)) - ret = true; - - return ret && !wasm_get_exception(module_inst) ? true : false; + return ret && !wasm_copy_exception(module_inst, NULL) ? true : false; } } #endif /* end of WASM_ENABLE_JIT != 0 */ @@ -4186,6 +4179,7 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMExecEnv *exec_env, unsigned frame_size = wasm_interp_interp_frame_size(all_cell_num); unsigned i; bool copy_argv_from_frame = true; + char exception[EXCEPTION_BUF_LEN]; if (argc < function->param_cell_num) { char buf[128]; @@ -4198,6 +4192,7 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMExecEnv *exec_env, } argc = function->param_cell_num; + RECORD_STACK_USAGE(exec_env, (uint8 *)&prev_frame); #if !(defined(OS_ENABLE_HW_BOUND_CHECK) \ && WASM_DISABLE_STACK_HW_BOUND_CHECK == 0) if ((uint8 *)&prev_frame < exec_env->native_stack_boundary) { @@ -4242,58 +4237,51 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMExecEnv *exec_env, } } else { -#if WASM_ENABLE_LAZY_JIT != 0 + RunningMode running_mode = + wasm_runtime_get_running_mode((wasm_module_inst_t)module_inst); - /* Fast JIT to LLVM JIT tier-up is enabled */ -#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 - /* Fast JIT and LLVM JIT are both enabled, call llvm jit function - if it is compiled, else call fast jit function */ - uint32 func_idx = (uint32)(function - module_inst->e->functions); - if (module_inst->module->func_ptrs_compiled - [func_idx - module_inst->module->import_function_count]) { + if (running_mode == Mode_Interp) { + wasm_interp_call_func_bytecode(module_inst, exec_env, function, + frame); + } +#if WASM_ENABLE_FAST_JIT != 0 + else if (running_mode == Mode_Fast_JIT) { + fast_jit_call_func_bytecode(module_inst, exec_env, function, frame); + } +#endif +#if WASM_ENABLE_JIT != 0 + else if (running_mode == Mode_LLVM_JIT) { llvm_jit_call_func_bytecode(module_inst, exec_env, function, argc, argv); /* For llvm jit, the results have been stored in argv, no need to copy them from stack frame again */ copy_argv_from_frame = false; } - else { - fast_jit_call_func_bytecode(module_inst, exec_env, function, frame); +#endif +#if WASM_ENABLE_LAZY_JIT != 0 && WASM_ENABLE_FAST_JIT != 0 \ + && WASM_ENABLE_JIT != 0 + else if (running_mode == Mode_Multi_Tier_JIT) { + /* Tier-up from Fast JIT to LLVM JIT, call llvm jit function + if it is compiled, else call fast jit function */ + uint32 func_idx = (uint32)(function - module_inst->e->functions); + if (module_inst->module->func_ptrs_compiled + [func_idx - module_inst->module->import_function_count]) { + llvm_jit_call_func_bytecode(module_inst, exec_env, function, + argc, argv); + /* For llvm jit, the results have been stored in argv, + no need to copy them from stack frame again */ + copy_argv_from_frame = false; + } + else { + fast_jit_call_func_bytecode(module_inst, exec_env, function, + frame); + } } -#elif WASM_ENABLE_JIT != 0 - /* Only LLVM JIT is enabled */ - llvm_jit_call_func_bytecode(module_inst, exec_env, function, argc, - argv); - /* For llvm jit, the results have been stored in argv, - no need to copy them from stack frame again */ - copy_argv_from_frame = false; -#elif WASM_ENABLE_FAST_JIT != 0 - /* Only Fast JIT is enabled */ - fast_jit_call_func_bytecode(module_inst, exec_env, function, frame); -#else - /* Both Fast JIT and LLVM JIT are disabled */ - wasm_interp_call_func_bytecode(module_inst, exec_env, function, frame); #endif - -#else /* else of WASM_ENABLE_LAZY_JIT != 0 */ - - /* Fast JIT to LLVM JIT tier-up is enabled */ -#if WASM_ENABLE_JIT != 0 - /* LLVM JIT is enabled */ - llvm_jit_call_func_bytecode(module_inst, exec_env, function, argc, - argv); - /* For llvm jit, the results have been stored in argv, - no need to copy them from stack frame again */ - copy_argv_from_frame = false; -#elif WASM_ENABLE_FAST_JIT != 0 - /* Fast JIT is enabled */ - fast_jit_call_func_bytecode(module_inst, exec_env, function, frame); -#else - /* Both Fast JIT and LLVM JIT are disabled */ - wasm_interp_call_func_bytecode(module_inst, exec_env, function, frame); -#endif - -#endif /* end of WASM_ENABLE_LAZY_JIT != 0 */ + else { + /* There should always be a supported running mode selected */ + bh_assert(0); + } (void)wasm_interp_call_func_bytecode; #if WASM_ENABLE_FAST_JIT != 0 @@ -4302,7 +4290,7 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMExecEnv *exec_env, } /* Output the return value to the caller */ - if (!wasm_get_exception(module_inst)) { + if (!wasm_copy_exception(module_inst, NULL)) { if (copy_argv_from_frame) { for (i = 0; i < function->ret_cell_num; i++) { argv[i] = *(frame->sp + i - function->ret_cell_num); @@ -4315,7 +4303,8 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMExecEnv *exec_env, wasm_interp_dump_call_stack(exec_env, true, NULL, 0); } #endif - LOG_DEBUG("meet an exception %s", wasm_get_exception(module_inst)); + wasm_copy_exception(module_inst, exception); + LOG_DEBUG("meet an exception %s", exception); } wasm_exec_env_set_cur_frame(exec_env, prev_frame); diff --git a/core/iwasm/interpreter/wasm_interp_fast.c b/core/iwasm/interpreter/wasm_interp_fast.c index eecf27992..d01ec2db4 100644 --- a/core/iwasm/interpreter/wasm_interp_fast.c +++ b/core/iwasm/interpreter/wasm_interp_fast.c @@ -8,6 +8,7 @@ #include "wasm_runtime.h" #include "wasm_opcode.h" #include "wasm_loader.h" +#include "wasm_memory.h" #include "../common/wasm_exec_env.h" #if WASM_ENABLE_SHARED_MEMORY != 0 #include "../common/wasm_shared_memory.h" @@ -469,28 +470,28 @@ LOAD_PTR(void *addr) CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 1, maddr); \ CHECK_ATOMIC_MEMORY_ACCESS(1); \ \ - os_mutex_lock(&module->e->mem_lock); \ + os_mutex_lock(&node->shared_mem_lock); \ readv = (uint32)(*(uint8 *)maddr); \ *(uint8 *)maddr = (uint8)(readv op sval); \ - os_mutex_unlock(&module->e->mem_lock); \ + os_mutex_unlock(&node->shared_mem_lock); \ } \ else if (opcode == WASM_OP_ATOMIC_RMW_I32_##OP_NAME##16_U) { \ CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 2, maddr); \ CHECK_ATOMIC_MEMORY_ACCESS(2); \ \ - os_mutex_lock(&module->e->mem_lock); \ + os_mutex_lock(&node->shared_mem_lock); \ readv = (uint32)LOAD_U16(maddr); \ STORE_U16(maddr, (uint16)(readv op sval)); \ - os_mutex_unlock(&module->e->mem_lock); \ + os_mutex_unlock(&node->shared_mem_lock); \ } \ else { \ CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 4, maddr); \ CHECK_ATOMIC_MEMORY_ACCESS(4); \ \ - os_mutex_lock(&module->e->mem_lock); \ + os_mutex_lock(&node->shared_mem_lock); \ readv = LOAD_I32(maddr); \ STORE_U32(maddr, readv op sval); \ - os_mutex_unlock(&module->e->mem_lock); \ + os_mutex_unlock(&node->shared_mem_lock); \ } \ PUSH_I32(readv); \ break; \ @@ -509,39 +510,39 @@ LOAD_PTR(void *addr) CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 1, maddr); \ CHECK_ATOMIC_MEMORY_ACCESS(1); \ \ - os_mutex_lock(&module->e->mem_lock); \ + os_mutex_lock(&node->shared_mem_lock); \ readv = (uint64)(*(uint8 *)maddr); \ *(uint8 *)maddr = (uint8)(readv op sval); \ - os_mutex_unlock(&module->e->mem_lock); \ + os_mutex_unlock(&node->shared_mem_lock); \ } \ else if (opcode == WASM_OP_ATOMIC_RMW_I64_##OP_NAME##16_U) { \ CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 2, maddr); \ CHECK_ATOMIC_MEMORY_ACCESS(2); \ \ - os_mutex_lock(&module->e->mem_lock); \ + os_mutex_lock(&node->shared_mem_lock); \ readv = (uint64)LOAD_U16(maddr); \ STORE_U16(maddr, (uint16)(readv op sval)); \ - os_mutex_unlock(&module->e->mem_lock); \ + os_mutex_unlock(&node->shared_mem_lock); \ } \ else if (opcode == WASM_OP_ATOMIC_RMW_I64_##OP_NAME##32_U) { \ CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 4, maddr); \ CHECK_ATOMIC_MEMORY_ACCESS(4); \ \ - os_mutex_lock(&module->e->mem_lock); \ + os_mutex_lock(&node->shared_mem_lock); \ readv = (uint64)LOAD_U32(maddr); \ STORE_U32(maddr, (uint32)(readv op sval)); \ - os_mutex_unlock(&module->e->mem_lock); \ + os_mutex_unlock(&node->shared_mem_lock); \ } \ else { \ uint64 op_result; \ CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 8, maddr); \ CHECK_ATOMIC_MEMORY_ACCESS(8); \ \ - os_mutex_lock(&module->e->mem_lock); \ + os_mutex_lock(&node->shared_mem_lock); \ readv = (uint64)LOAD_I64(maddr); \ op_result = readv op sval; \ STORE_I64(maddr, op_result); \ - os_mutex_unlock(&module->e->mem_lock); \ + os_mutex_unlock(&node->shared_mem_lock); \ } \ PUSH_I64(readv); \ break; \ @@ -925,7 +926,7 @@ wasm_interp_call_func_native(WASMModuleInstance *module_inst, if (!func_import->call_conv_wasm_c_api) { native_func_pointer = module_inst->import_func_ptrs[cur_func_index]; } - else { + else if (module_inst->e->c_api_func_imports) { c_api_func_import = module_inst->e->c_api_func_imports + cur_func_index; native_func_pointer = c_api_func_import->func_ptr_linked; } @@ -1042,7 +1043,7 @@ wasm_interp_call_func_import(WASMModuleInstance *module_inst, exec_env->module_inst = (WASMModuleInstanceCommon *)module_inst; /* transfer exception if it is thrown */ - if (wasm_get_exception(sub_module_inst)) { + if (wasm_copy_exception(sub_module_inst, NULL)) { bh_memcpy_s(module_inst->cur_exception, sizeof(module_inst->cur_exception), sub_module_inst->cur_exception, @@ -1054,13 +1055,16 @@ wasm_interp_call_func_import(WASMModuleInstance *module_inst, #if WASM_ENABLE_THREAD_MGR != 0 #define CHECK_SUSPEND_FLAGS() \ do { \ + os_mutex_lock(&exec_env->wait_lock); \ if (exec_env->suspend_flags.flags != 0) { \ if (exec_env->suspend_flags.flags & 0x01) { \ /* terminate current thread */ \ + os_mutex_unlock(&exec_env->wait_lock); \ return; \ } \ /* TODO: support suspend and breakpoint */ \ } \ + os_mutex_unlock(&exec_env->wait_lock); \ } while (0) #endif @@ -1151,13 +1155,22 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, WASMFunctionInstance *cur_func, WASMInterpFrame *prev_frame) { +#if WASM_ENABLE_SHARED_MEMORY != 0 + WASMSharedMemNode *node = + wasm_module_get_shared_memory((WASMModuleCommon *)module->module); +#else + void *node = NULL; +#endif + WASMMemoryInstance *memory = wasm_get_default_memory(module); + #if !defined(OS_ENABLE_HW_BOUND_CHECK) \ || WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 \ || WASM_ENABLE_BULK_MEMORY != 0 - uint32 num_bytes_per_page = memory ? memory->num_bytes_per_page : 0; + uint32 num_bytes_per_page = + memory ? wasm_get_num_bytes_per_page(memory, node) : 0; uint32 linear_mem_size = - memory ? num_bytes_per_page * memory->cur_page_count : 0; + memory ? wasm_get_linear_memory_size(memory, node) : 0; #endif uint8 *global_data = module->global_data; WASMGlobalInstance *globals = module->e ? module->e->globals : NULL; @@ -3275,6 +3288,10 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, if (ret == (uint32)-1) goto got_exception; +#if WASM_ENABLE_THREAD_MGR != 0 + CHECK_SUSPEND_FLAGS(); +#endif + PUSH_I32(ret); break; } @@ -3295,9 +3312,18 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, if (ret == (uint32)-1) goto got_exception; +#if WASM_ENABLE_THREAD_MGR != 0 + CHECK_SUSPEND_FLAGS(); +#endif + PUSH_I32(ret); break; } + case WASM_OP_ATOMIC_FENCE: + { + os_atomic_thread_fence(os_memory_order_release); + break; + } case WASM_OP_ATOMIC_I32_LOAD: case WASM_OP_ATOMIC_I32_LOAD8_U: @@ -3310,23 +3336,23 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, if (opcode == WASM_OP_ATOMIC_I32_LOAD8_U) { CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 1, maddr); CHECK_ATOMIC_MEMORY_ACCESS(1); - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); readv = (uint32)(*(uint8 *)maddr); - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } else if (opcode == WASM_OP_ATOMIC_I32_LOAD16_U) { CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 2, maddr); CHECK_ATOMIC_MEMORY_ACCESS(2); - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); readv = (uint32)LOAD_U16(maddr); - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } else { CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 4, maddr); CHECK_ATOMIC_MEMORY_ACCESS(4); - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); readv = LOAD_I32(maddr); - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } PUSH_I32(readv); @@ -3345,30 +3371,30 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, if (opcode == WASM_OP_ATOMIC_I64_LOAD8_U) { CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 1, maddr); CHECK_ATOMIC_MEMORY_ACCESS(1); - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); readv = (uint64)(*(uint8 *)maddr); - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } else if (opcode == WASM_OP_ATOMIC_I64_LOAD16_U) { CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 2, maddr); CHECK_ATOMIC_MEMORY_ACCESS(2); - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); readv = (uint64)LOAD_U16(maddr); - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } else if (opcode == WASM_OP_ATOMIC_I64_LOAD32_U) { CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 4, maddr); CHECK_ATOMIC_MEMORY_ACCESS(4); - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); readv = (uint64)LOAD_U32(maddr); - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } else { CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 8, maddr); CHECK_ATOMIC_MEMORY_ACCESS(8); - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); readv = LOAD_I64(maddr); - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } PUSH_I64(readv); @@ -3386,23 +3412,23 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, if (opcode == WASM_OP_ATOMIC_I32_STORE8) { CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 1, maddr); CHECK_ATOMIC_MEMORY_ACCESS(1); - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); *(uint8 *)maddr = (uint8)sval; - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } else if (opcode == WASM_OP_ATOMIC_I32_STORE16) { CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 2, maddr); CHECK_ATOMIC_MEMORY_ACCESS(2); - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); STORE_U16(maddr, (uint16)sval); - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } else { CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 4, maddr); CHECK_ATOMIC_MEMORY_ACCESS(4); - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); STORE_U32(maddr, sval); - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } break; } @@ -3420,30 +3446,30 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, if (opcode == WASM_OP_ATOMIC_I64_STORE8) { CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 1, maddr); CHECK_ATOMIC_MEMORY_ACCESS(1); - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); *(uint8 *)maddr = (uint8)sval; - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } else if (opcode == WASM_OP_ATOMIC_I64_STORE16) { CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 2, maddr); CHECK_ATOMIC_MEMORY_ACCESS(2); - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); STORE_U16(maddr, (uint16)sval); - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } else if (opcode == WASM_OP_ATOMIC_I64_STORE32) { CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 4, maddr); CHECK_ATOMIC_MEMORY_ACCESS(4); - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); STORE_U32(maddr, (uint32)sval); - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } else { CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 8, maddr); CHECK_ATOMIC_MEMORY_ACCESS(8); - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); STORE_I64(maddr, sval); - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } break; } @@ -3463,32 +3489,32 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, CHECK_ATOMIC_MEMORY_ACCESS(1); expect = (uint8)expect; - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); readv = (uint32)(*(uint8 *)maddr); if (readv == expect) *(uint8 *)maddr = (uint8)(sval); - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } else if (opcode == WASM_OP_ATOMIC_RMW_I32_CMPXCHG16_U) { CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 2, maddr); CHECK_ATOMIC_MEMORY_ACCESS(2); expect = (uint16)expect; - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); readv = (uint32)LOAD_U16(maddr); if (readv == expect) STORE_U16(maddr, (uint16)(sval)); - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } else { CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 4, maddr); CHECK_ATOMIC_MEMORY_ACCESS(4); - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); readv = LOAD_I32(maddr); if (readv == expect) STORE_U32(maddr, sval); - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } PUSH_I32(readv); break; @@ -3509,44 +3535,44 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, CHECK_ATOMIC_MEMORY_ACCESS(1); expect = (uint8)expect; - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); readv = (uint64)(*(uint8 *)maddr); if (readv == expect) *(uint8 *)maddr = (uint8)(sval); - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } else if (opcode == WASM_OP_ATOMIC_RMW_I64_CMPXCHG16_U) { CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 2, maddr); CHECK_ATOMIC_MEMORY_ACCESS(2); expect = (uint16)expect; - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); readv = (uint64)LOAD_U16(maddr); if (readv == expect) STORE_U16(maddr, (uint16)(sval)); - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } else if (opcode == WASM_OP_ATOMIC_RMW_I64_CMPXCHG32_U) { CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 4, maddr); CHECK_ATOMIC_MEMORY_ACCESS(4); expect = (uint32)expect; - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); readv = (uint64)LOAD_U32(maddr); if (readv == expect) STORE_U32(maddr, (uint32)(sval)); - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } else { CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 8, maddr); CHECK_ATOMIC_MEMORY_ACCESS(8); - os_mutex_lock(&module->e->mem_lock); + os_mutex_lock(&node->shared_mem_lock); readv = (uint64)LOAD_I64(maddr); if (readv == expect) { STORE_I64(maddr, sval); } - os_mutex_unlock(&module->e->mem_lock); + os_mutex_unlock(&node->shared_mem_lock); } PUSH_I64(readv); break; @@ -3797,7 +3823,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, if (memory) linear_mem_size = num_bytes_per_page * memory->cur_page_count; #endif - if (wasm_get_exception(module)) + if (wasm_copy_exception(module, NULL)) goto got_exception; } else { @@ -3837,6 +3863,9 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, wasm_exec_env_set_cur_frame(exec_env, (WASMRuntimeFrame *)frame); } +#if WASM_ENABLE_THREAD_MGR != 0 + CHECK_SUSPEND_FLAGS(); +#endif HANDLE_OP_END(); } @@ -3905,6 +3934,7 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMExecEnv *exec_env, /* This frame won't be used by JITed code, so only allocate interp frame here. */ unsigned frame_size = wasm_interp_interp_frame_size(all_cell_num); + char exception[EXCEPTION_BUF_LEN]; if (argc < function->param_cell_num) { char buf[128]; @@ -3917,6 +3947,7 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMExecEnv *exec_env, } argc = function->param_cell_num; + RECORD_STACK_USAGE(exec_env, (uint8 *)&prev_frame); #if !(defined(OS_ENABLE_HW_BOUND_CHECK) \ && WASM_DISABLE_STACK_HW_BOUND_CHECK == 0) if ((uint8 *)&prev_frame < exec_env->native_stack_boundary) { @@ -3969,7 +4000,7 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMExecEnv *exec_env, } /* Output the return value to the caller */ - if (!wasm_get_exception(module_inst)) { + if (!wasm_copy_exception(module_inst, NULL)) { for (i = 0; i < function->ret_cell_num; i++) argv[i] = *(frame->lp + i); } @@ -3979,7 +4010,8 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMExecEnv *exec_env, wasm_interp_dump_call_stack(exec_env, true, NULL, 0); } #endif - LOG_DEBUG("meet an exception %s", wasm_get_exception(module_inst)); + wasm_copy_exception(module_inst, exception); + LOG_DEBUG("meet an exception %s", exception); } wasm_exec_env_set_cur_frame(exec_env, prev_frame); diff --git a/core/iwasm/interpreter/wasm_loader.c b/core/iwasm/interpreter/wasm_loader.c index b4b8d823c..87d43be5f 100644 --- a/core/iwasm/interpreter/wasm_loader.c +++ b/core/iwasm/interpreter/wasm_loader.c @@ -1399,6 +1399,7 @@ load_global_import(const uint8 **p_buf, const uint8 *buf_end, WASMModule *sub_module = NULL; WASMGlobal *linked_global = NULL; #endif + bool ret = false; CHECK_BUF(p, p_end, 2); declare_type = read_uint8(p); @@ -1411,15 +1412,16 @@ load_global_import(const uint8 **p_buf, const uint8 *buf_end, } #if WASM_ENABLE_LIBC_BUILTIN != 0 - global->is_linked = wasm_native_lookup_libc_builtin_global( - sub_module_name, global_name, global); - if (global->is_linked) { + ret = wasm_native_lookup_libc_builtin_global(sub_module_name, global_name, + global); + if (ret) { if (global->type != declare_type || global->is_mutable != declare_mutable) { set_error_buf(error_buf, error_buf_size, "incompatible import type"); return false; } + global->is_linked = true; } #endif #if WASM_ENABLE_MULTI_MODULE != 0 @@ -1449,6 +1451,7 @@ load_global_import(const uint8 **p_buf, const uint8 *buf_end, global->is_mutable = (declare_mutable == 1); (void)parent_module; + (void)ret; return true; fail: return false; @@ -2989,6 +2992,7 @@ static bool init_llvm_jit_functions_stage1(WASMModule *module, char *error_buf, uint32 error_buf_size) { + LLVMJITOptions llvm_jit_options = wasm_runtime_get_llvm_jit_options(); AOTCompOption option = { 0 }; char *aot_last_error; uint64 size; @@ -3027,8 +3031,11 @@ init_llvm_jit_functions_stage1(WASMModule *module, char *error_buf, } option.is_jit_mode = true; - option.opt_level = 3; - option.size_level = 3; + + llvm_jit_options = wasm_runtime_get_llvm_jit_options(); + option.opt_level = llvm_jit_options.opt_level; + option.size_level = llvm_jit_options.size_level; + #if WASM_ENABLE_BULK_MEMORY != 0 option.enable_bulk_memory = true; #endif @@ -3048,6 +3055,9 @@ init_llvm_jit_functions_stage1(WASMModule *module, char *error_buf, #if (WASM_ENABLE_PERF_PROFILING != 0) || (WASM_ENABLE_DUMP_CALL_STACK != 0) option.enable_aux_stack_frame = true; #endif +#if WASM_ENABLE_MEMORY_PROFILING != 0 + option.enable_stack_estimation = true; +#endif module->comp_ctx = aot_create_comp_context(module->comp_data, &option); if (!module->comp_ctx) { @@ -3109,6 +3119,8 @@ init_llvm_jit_functions_stage2(WASMModule *module, char *error_buf, module->func_ptrs[i] = (void *)func_addr; #if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_LAZY_JIT != 0 + module->functions[i]->llvm_jit_func_ptr = (void *)func_addr; + if (module->orcjit_stop_compiling) return false; #endif @@ -3199,9 +3211,9 @@ orcjit_thread_callback(void *arg) /* Wait until init_llvm_jit_functions_stage2 finishes */ os_mutex_lock(&module->tierup_wait_lock); - while (!module->llvm_jit_inited) { + while (!(module->llvm_jit_inited && module->enable_llvm_jit_compilation)) { os_cond_reltimedwait(&module->tierup_wait_cond, - &module->tierup_wait_lock, 10); + &module->tierup_wait_lock, 10000); if (module->orcjit_stop_compiling) { /* init_llvm_jit_functions_stage2 failed */ os_mutex_unlock(&module->tierup_wait_lock); @@ -3852,8 +3864,8 @@ create_module(char *error_buf, uint32 error_buf_size) bh_assert(ret == BH_LIST_SUCCESS); #endif -#if WASM_ENABLE_DEBUG_INTERP != 0 \ - || (WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT \ +#if WASM_ENABLE_DEBUG_INTERP != 0 \ + || (WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ && WASM_ENABLE_LAZY_JIT != 0) if (os_mutex_init(&module->instance_list_lock) != 0) { set_error_buf(error_buf, error_buf_size, @@ -4148,10 +4160,8 @@ check_wasi_abi_compatibility(const WASMModule *module, /* should have one at least */ if (module->import_wasi_api && !start && !initialize) { - set_error_buf( - error_buf, error_buf_size, - "a module with WASI apis must be either a command or a reactor"); - return false; + LOG_WARNING("warning: a module with WASI apis should be either " + "a command or a reactor"); } /* @@ -4185,7 +4195,20 @@ check_wasi_abi_compatibility(const WASMModule *module, memory = wasm_loader_find_export(module, "", "memory", EXPORT_KIND_MEMORY, error_buf, error_buf_size); - if (!memory) { + if (!memory +#if WASM_ENABLE_LIB_WASI_THREADS != 0 + /* + * with wasi-threads, it's still an open question if a memory + * should be exported. + * + * https://github.com/WebAssembly/wasi-threads/issues/22 + * https://github.com/WebAssembly/WASI/issues/502 + * + * Note: this code assumes the number of memories is at most 1. + */ + && module->import_memory_count == 0 +#endif + ) { set_error_buf(error_buf, error_buf_size, "a module with WASI apis must export memory by default"); return false; @@ -4243,7 +4266,8 @@ wasm_loader_unload(WASMModule *module) if (!module) return; -#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT && WASM_ENABLE_LAZY_JIT != 0 +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ + && WASM_ENABLE_LAZY_JIT != 0 module->orcjit_stop_compiling = true; if (module->llvm_jit_init_thread) os_thread_join(module->llvm_jit_init_thread, NULL); @@ -4264,7 +4288,8 @@ wasm_loader_unload(WASMModule *module) aot_destroy_comp_data(module->comp_data); #endif -#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT && WASM_ENABLE_LAZY_JIT != 0 +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ + && WASM_ENABLE_LAZY_JIT != 0 if (module->tierup_wait_lock_inited) { os_mutex_destroy(&module->tierup_wait_lock); os_cond_destroy(&module->tierup_wait_cond); @@ -4299,9 +4324,9 @@ wasm_loader_unload(WASMModule *module) module->functions[i]->fast_jit_jitted_code); } #if WASM_ENABLE_JIT != 0 && WASM_ENABLE_LAZY_JIT != 0 - if (module->functions[i]->llvm_jit_func_ptr) { + if (module->functions[i]->call_to_fast_jit_from_llvm_jit) { jit_code_cache_free( - module->functions[i]->llvm_jit_func_ptr); + module->functions[i]->call_to_fast_jit_from_llvm_jit); } #endif #endif @@ -4393,8 +4418,8 @@ wasm_loader_unload(WASMModule *module) } #endif -#if WASM_ENABLE_DEBUG_INTERP != 0 \ - || (WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT \ +#if WASM_ENABLE_DEBUG_INTERP != 0 \ + || (WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ && WASM_ENABLE_LAZY_JIT != 0) os_mutex_destroy(&module->instance_list_lock); #endif diff --git a/core/iwasm/interpreter/wasm_mini_loader.c b/core/iwasm/interpreter/wasm_mini_loader.c index b481a339b..6be298665 100644 --- a/core/iwasm/interpreter/wasm_mini_loader.c +++ b/core/iwasm/interpreter/wasm_mini_loader.c @@ -1835,6 +1835,7 @@ static bool init_llvm_jit_functions_stage1(WASMModule *module, char *error_buf, uint32 error_buf_size) { + LLVMJITOptions llvm_jit_options = wasm_runtime_get_llvm_jit_options(); AOTCompOption option = { 0 }; char *aot_last_error; uint64 size; @@ -1873,8 +1874,9 @@ init_llvm_jit_functions_stage1(WASMModule *module, char *error_buf, } option.is_jit_mode = true; - option.opt_level = 3; - option.size_level = 3; + option.opt_level = llvm_jit_options.opt_level; + option.size_level = llvm_jit_options.size_level; + #if WASM_ENABLE_BULK_MEMORY != 0 option.enable_bulk_memory = true; #endif @@ -1894,6 +1896,9 @@ init_llvm_jit_functions_stage1(WASMModule *module, char *error_buf, #if (WASM_ENABLE_PERF_PROFILING != 0) || (WASM_ENABLE_DUMP_CALL_STACK != 0) option.enable_aux_stack_frame = true; #endif +#if WASM_ENABLE_MEMORY_PROFILING != 0 + option.enable_stack_estimation = true; +#endif module->comp_ctx = aot_create_comp_context(module->comp_data, &option); if (!module->comp_ctx) { @@ -1957,6 +1962,8 @@ init_llvm_jit_functions_stage2(WASMModule *module, char *error_buf, module->func_ptrs[i] = (void *)func_addr; #if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_LAZY_JIT != 0 + module->functions[i]->llvm_jit_func_ptr = (void *)func_addr; + if (module->orcjit_stop_compiling) return false; #endif @@ -2047,9 +2054,9 @@ orcjit_thread_callback(void *arg) /* Wait until init_llvm_jit_functions_stage2 finishes */ os_mutex_lock(&module->tierup_wait_lock); - while (!module->llvm_jit_inited) { + while (!(module->llvm_jit_inited && module->enable_llvm_jit_compilation)) { os_cond_reltimedwait(&module->tierup_wait_cond, - &module->tierup_wait_lock, 10); + &module->tierup_wait_lock, 10000); if (module->orcjit_stop_compiling) { /* init_llvm_jit_functions_stage2 failed */ os_mutex_unlock(&module->tierup_wait_lock); @@ -2718,7 +2725,8 @@ create_module(char *error_buf, uint32 error_buf_size) bh_assert(ret == BH_LIST_SUCCESS); #endif -#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT && WASM_ENABLE_LAZY_JIT != 0 +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ + && WASM_ENABLE_LAZY_JIT != 0 if (os_mutex_init(&module->instance_list_lock) != 0) { set_error_buf(error_buf, error_buf_size, "init instance list lock failed"); @@ -2939,7 +2947,8 @@ wasm_loader_unload(WASMModule *module) if (!module) return; -#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT && WASM_ENABLE_LAZY_JIT != 0 +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ + && WASM_ENABLE_LAZY_JIT != 0 module->orcjit_stop_compiling = true; if (module->llvm_jit_init_thread) os_thread_join(module->llvm_jit_init_thread, NULL); @@ -2960,7 +2969,8 @@ wasm_loader_unload(WASMModule *module) aot_destroy_comp_data(module->comp_data); #endif -#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT && WASM_ENABLE_LAZY_JIT != 0 +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ + && WASM_ENABLE_LAZY_JIT != 0 if (module->tierup_wait_lock_inited) { os_mutex_destroy(&module->tierup_wait_lock); os_cond_destroy(&module->tierup_wait_cond); @@ -2995,9 +3005,9 @@ wasm_loader_unload(WASMModule *module) module->functions[i]->fast_jit_jitted_code); } #if WASM_ENABLE_JIT != 0 && WASM_ENABLE_LAZY_JIT != 0 - if (module->functions[i]->llvm_jit_func_ptr) { + if (module->functions[i]->call_to_fast_jit_from_llvm_jit) { jit_code_cache_free( - module->functions[i]->llvm_jit_func_ptr); + module->functions[i]->call_to_fast_jit_from_llvm_jit); } #endif #endif @@ -3056,7 +3066,8 @@ wasm_loader_unload(WASMModule *module) } #endif -#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT && WASM_ENABLE_LAZY_JIT != 0 +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ + && WASM_ENABLE_LAZY_JIT != 0 os_mutex_destroy(&module->instance_list_lock); #endif diff --git a/core/iwasm/interpreter/wasm_opcode.h b/core/iwasm/interpreter/wasm_opcode.h index 067deed82..287a570c7 100644 --- a/core/iwasm/interpreter/wasm_opcode.h +++ b/core/iwasm/interpreter/wasm_opcode.h @@ -677,12 +677,14 @@ typedef enum WASMAtomicEXTOpcode { } WASMAtomicEXTOpcode; #if WASM_ENABLE_DEBUG_INTERP != 0 -#define DEF_DEBUG_BREAK_HANDLE(_name) \ - _name[DEBUG_OP_BREAK] = HANDLE_OPCODE(DEBUG_OP_BREAK); /* 0xd7 */ +#define DEF_DEBUG_BREAK_HANDLE() \ + [DEBUG_OP_BREAK] = HANDLE_OPCODE(DEBUG_OP_BREAK), /* 0xd7 */ #else -#define DEF_DEBUG_BREAK_HANDLE(_name) +#define DEF_DEBUG_BREAK_HANDLE() #endif +#define SET_GOTO_TABLE_ELEM(opcode) [opcode] = HANDLE_OPCODE(opcode) + /* * Macro used to generate computed goto tables for the C interpreter. */ @@ -905,14 +907,10 @@ typedef enum WASMAtomicEXTOpcode { HANDLE_OPCODE(EXT_OP_LOOP), /* 0xd4 */ \ HANDLE_OPCODE(EXT_OP_IF), /* 0xd5 */ \ HANDLE_OPCODE(EXT_OP_BR_TABLE_CACHE), /* 0xd6 */ \ - }; \ - do { \ - _name[WASM_OP_MISC_PREFIX] = \ - HANDLE_OPCODE(WASM_OP_MISC_PREFIX); /* 0xfc */ \ - _name[WASM_OP_ATOMIC_PREFIX] = \ - HANDLE_OPCODE(WASM_OP_ATOMIC_PREFIX); /* 0xfe */ \ - DEF_DEBUG_BREAK_HANDLE(_name) \ - } while (0) + SET_GOTO_TABLE_ELEM(WASM_OP_MISC_PREFIX), /* 0xfc */ \ + SET_GOTO_TABLE_ELEM(WASM_OP_ATOMIC_PREFIX), /* 0xfe */ \ + DEF_DEBUG_BREAK_HANDLE() \ + }; #ifdef __cplusplus } diff --git a/core/iwasm/interpreter/wasm_runtime.c b/core/iwasm/interpreter/wasm_runtime.c index fd85b71b8..9f08a484d 100644 --- a/core/iwasm/interpreter/wasm_runtime.c +++ b/core/iwasm/interpreter/wasm_runtime.c @@ -737,13 +737,12 @@ functions_instantiate(const WASMModule *module, WASMModuleInstance *module_inst, function++; } + bh_assert((uint32)(function - functions) == function_count); #if WASM_ENABLE_FAST_JIT != 0 module_inst->fast_jit_func_ptrs = module->fast_jit_func_ptrs; #endif - bh_assert((uint32)(function - functions) == function_count); - (void)module_inst; return functions; } @@ -983,88 +982,138 @@ export_globals_instantiate(const WASMModule *module, } #endif -#if WASM_ENABLE_LIBC_WASI != 0 -static bool -execute_initialize_function(WASMModuleInstance *module_inst) +static WASMFunctionInstance * +lookup_post_instantiate_func(WASMModuleInstance *module_inst, + const char *func_name) { - WASMFunctionInstance *initialize = - wasm_lookup_function(module_inst, "_initialize", NULL); - return !initialize - || wasm_create_exec_env_and_call_function(module_inst, initialize, 0, - NULL); + WASMFunctionInstance *func; + WASMType *func_type; + + if (!(func = wasm_lookup_function(module_inst, func_name, NULL))) + /* Not found */ + return NULL; + + func_type = func->u.func->func_type; + if (!(func_type->param_count == 0 && func_type->result_count == 0)) + /* Not a valid function type, ignore it */ + return NULL; + + return func; } + +static bool +execute_post_instantiate_functions(WASMModuleInstance *module_inst, + bool is_sub_inst) +{ + WASMFunctionInstance *start_func = module_inst->e->start_function; + WASMFunctionInstance *initialize_func = NULL; + WASMFunctionInstance *post_inst_func = NULL; + WASMFunctionInstance *call_ctors_func = NULL; +#if WASM_ENABLE_LIBC_WASI != 0 + WASMModule *module = module_inst->module; +#endif +#ifdef OS_ENABLE_HW_BOUND_CHECK + WASMModuleInstanceCommon *module_inst_main = NULL; + WASMExecEnv *exec_env_tls = NULL; +#endif + WASMExecEnv *exec_env = NULL; + bool ret = false; + +#if WASM_ENABLE_LIBC_WASI != 0 + /* + * WASI reactor instances may assume that _initialize will be called by + * the environment at most once, and that none of their other exports + * are accessed before that call. + */ + if (!is_sub_inst && module->import_wasi_api) { + initialize_func = + lookup_post_instantiate_func(module_inst, "_initialize"); + } #endif -static bool -execute_post_inst_function(WASMModuleInstance *module_inst) -{ - WASMFunctionInstance *post_inst_func = NULL; - WASMType *post_inst_func_type; - uint32 i; - - for (i = 0; i < module_inst->export_func_count; i++) - if (!strcmp(module_inst->export_functions[i].name, - "__post_instantiate")) { - post_inst_func = module_inst->export_functions[i].function; - break; - } - - if (!post_inst_func) - /* Not found */ - return true; - - post_inst_func_type = post_inst_func->u.func->func_type; - if (post_inst_func_type->param_count != 0 - || post_inst_func_type->result_count != 0) - /* Not a valid function type, ignore it */ - return true; - - return wasm_create_exec_env_and_call_function(module_inst, post_inst_func, - 0, NULL); -} + /* Execute possible "__post_instantiate" function if wasm app is + compiled by emsdk's early version */ + if (!is_sub_inst) { + post_inst_func = + lookup_post_instantiate_func(module_inst, "__post_instantiate"); + } #if WASM_ENABLE_BULK_MEMORY != 0 -static bool -execute_memory_init_function(WASMModuleInstance *module_inst) -{ - WASMFunctionInstance *memory_init_func = NULL; - WASMType *memory_init_func_type; - uint32 i; - - for (i = 0; i < module_inst->export_func_count; i++) - if (!strcmp(module_inst->export_functions[i].name, - "__wasm_call_ctors")) { - memory_init_func = module_inst->export_functions[i].function; - break; - } - - if (!memory_init_func) - /* Not found */ - return true; - - memory_init_func_type = memory_init_func->u.func->func_type; - if (memory_init_func_type->param_count != 0 - || memory_init_func_type->result_count != 0) - /* Not a valid function type, ignore it */ - return true; - - return wasm_create_exec_env_and_call_function(module_inst, memory_init_func, - 0, NULL); -} + /* Only execute the memory init function for main instance since + the data segments will be dropped once initialized */ + if (!is_sub_inst +#if WASM_ENABLE_LIBC_WASI != 0 + && !module->import_wasi_api +#endif + ) { + call_ctors_func = + lookup_post_instantiate_func(module_inst, "__wasm_call_ctors"); + } #endif -static bool -execute_start_function(WASMModuleInstance *module_inst) -{ - WASMFunctionInstance *func = module_inst->e->start_function; - - if (!func) + if (!start_func && !initialize_func && !post_inst_func + && !call_ctors_func) { + /* No post instantiation functions to call */ return true; + } - bh_assert(!func->is_import_func && func->param_cell_num == 0 - && func->ret_cell_num == 0); +#ifdef OS_ENABLE_HW_BOUND_CHECK + if (is_sub_inst) { + exec_env = exec_env_tls = wasm_runtime_get_exec_env_tls(); + if (exec_env_tls) { + /* Temporarily replace exec_env_tls's module inst to current + module inst to avoid checking failure when calling the + wasm functions, and ensure that the exec_env's module inst + is the correct one. */ + module_inst_main = exec_env_tls->module_inst; + exec_env_tls->module_inst = (WASMModuleInstanceCommon *)module_inst; + } + } +#endif + if (!exec_env + && !(exec_env = + wasm_exec_env_create((WASMModuleInstanceCommon *)module_inst, + module_inst->default_wasm_stack_size))) { + wasm_set_exception(module_inst, "allocate memory failed"); + return false; + } - return wasm_create_exec_env_and_call_function(module_inst, func, 0, NULL); + /* Execute start function for both main insance and sub instance */ + if (start_func && !wasm_call_function(exec_env, start_func, 0, NULL)) { + goto fail; + } + + if (initialize_func + && !wasm_call_function(exec_env, initialize_func, 0, NULL)) { + goto fail; + } + + if (post_inst_func + && !wasm_call_function(exec_env, post_inst_func, 0, NULL)) { + goto fail; + } + + if (call_ctors_func + && !wasm_call_function(exec_env, call_ctors_func, 0, NULL)) { + goto fail; + } + + ret = true; + +fail: +#ifdef OS_ENABLE_HW_BOUND_CHECK + if (is_sub_inst && exec_env_tls) { + bh_assert(exec_env == exec_env_tls); + /* Restore the exec_env_tls's module inst */ + exec_env_tls->module_inst = module_inst_main; + } + else + wasm_exec_env_destroy(exec_env); +#else + wasm_exec_env_destroy(exec_env); +#endif + + return ret; } static bool @@ -1235,6 +1284,7 @@ check_linked_symbol(WASMModuleInstance *module_inst, char *error_buf, #if WASM_ENABLE_WAMR_COMPILER == 0 LOG_WARNING("warning: failed to link import function (%s, %s)", func->module_name, func->field_name); + /* will throw exception only if calling */ #else /* do nothing to avoid confused message */ #endif /* WASM_ENABLE_WAMR_COMPILER == 0 */ @@ -1250,8 +1300,10 @@ check_linked_symbol(WASMModuleInstance *module_inst, char *error_buf, return false; #else #if WASM_ENABLE_WAMR_COMPILER == 0 - LOG_DEBUG("warning: failed to link import global (%s, %s)", - global->module_name, global->field_name); + set_error_buf_v(error_buf, error_buf_size, + "failed to link import global (%s, %s)", + global->module_name, global->field_name); + return false; #else /* do nothing to avoid confused message */ #endif /* WASM_ENABLE_WAMR_COMPILER == 0 */ @@ -1285,9 +1337,8 @@ init_func_ptrs(WASMModuleInstance *module_inst, WASMModule *module, *func_ptrs = import_func->func_ptr_linked; } - /* Set defined function pointers */ - bh_memcpy_s(func_ptrs, sizeof(void *) * module->function_count, - module->func_ptrs, sizeof(void *) * module->function_count); + /* The defined function pointers will be set in + wasm_runtime_set_running_mode, no need to set them here */ return true; } #endif /* end of WASM_ENABLE_JIT != 0 */ @@ -1333,6 +1384,173 @@ init_func_type_indexes(WASMModuleInstance *module_inst, char *error_buf, } #endif /* end of WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 */ +static bool +set_running_mode(WASMModuleInstance *module_inst, RunningMode running_mode, + bool first_time_set) +{ + WASMModule *module = module_inst->module; + + if (running_mode == Mode_Default) { +#if WASM_ENABLE_FAST_JIT == 0 && WASM_ENABLE_JIT == 0 + running_mode = Mode_Interp; +#elif WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT == 0 + running_mode = Mode_Fast_JIT; +#elif WASM_ENABLE_FAST_JIT == 0 && WASM_ENABLE_JIT != 0 + running_mode = Mode_LLVM_JIT; +#else /* WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 */ +#if WASM_ENABLE_LAZY_JIT == 0 + running_mode = Mode_LLVM_JIT; +#else + running_mode = Mode_Multi_Tier_JIT; +#endif +#endif + } + + if (!wasm_runtime_is_running_mode_supported(running_mode)) + return false; + +#if !(WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ + && WASM_ENABLE_LAZY_JIT != 0) /* No possible multi-tier JIT */ + module_inst->e->running_mode = running_mode; + + if (running_mode == Mode_Interp) { + /* Do nothing for Mode_Interp */ + } + else if (running_mode == Mode_Fast_JIT) { + /* Do nothing for Mode_Fast_JIT since + module_inst->fast_jit_func_ptrs is same as + module->fast_jit_func_ptrs */ + } +#if WASM_ENABLE_JIT != 0 + else if (running_mode == Mode_LLVM_JIT) { + /* Set defined function pointers */ + bh_memcpy_s(module_inst->func_ptrs + module->import_function_count, + sizeof(void *) * module->function_count, module->func_ptrs, + sizeof(void *) * module->function_count); + } +#endif + else { + bh_assert(0); + } +#else /* Possible multi-tier JIT */ + os_mutex_lock(&module->instance_list_lock); + + module_inst->e->running_mode = running_mode; + + if (running_mode == Mode_Interp) { + /* Do nothing for Mode_Interp */ + } +#if WASM_ENABLE_FAST_JIT != 0 + else if (running_mode == Mode_Fast_JIT) { + JitGlobals *jit_globals = jit_compiler_get_jit_globals(); + uint32 i; + + /* Allocate memory for fast_jit_func_ptrs if needed */ + if (!module_inst->fast_jit_func_ptrs + || module_inst->fast_jit_func_ptrs == module->fast_jit_func_ptrs) { + uint64 total_size = (uint64)sizeof(void *) * module->function_count; + if (!(module_inst->fast_jit_func_ptrs = + runtime_malloc(total_size, NULL, 0))) { + os_mutex_unlock(&module->instance_list_lock); + return false; + } + } + + for (i = 0; i < module->function_count; i++) { + if (module->functions[i]->fast_jit_jitted_code) { + /* current fast jit function has been compiled */ + module_inst->fast_jit_func_ptrs[i] = + module->functions[i]->fast_jit_jitted_code; + } + else { + module_inst->fast_jit_func_ptrs[i] = + jit_globals->compile_fast_jit_and_then_call; + } + } + } +#endif +#if WASM_ENABLE_JIT != 0 + else if (running_mode == Mode_LLVM_JIT) { + void **llvm_jit_func_ptrs; + uint32 i; + + /* Notify backend threads to start llvm jit compilation */ + module->enable_llvm_jit_compilation = true; + + /* Wait until llvm jit finishes initialization */ + os_mutex_lock(&module->tierup_wait_lock); + while (!module->llvm_jit_inited) { + os_cond_reltimedwait(&module->tierup_wait_cond, + &module->tierup_wait_lock, 10); + if (module->orcjit_stop_compiling) { + /* init_llvm_jit_functions_stage2 failed */ + os_mutex_unlock(&module->tierup_wait_lock); + os_mutex_unlock(&module->instance_list_lock); + return false; + } + } + os_mutex_unlock(&module->tierup_wait_lock); + + llvm_jit_func_ptrs = + module_inst->func_ptrs + module->import_function_count; + for (i = 0; i < module->function_count; i++) { + llvm_jit_func_ptrs[i] = module->functions[i]->llvm_jit_func_ptr; + } + } +#endif + else if (running_mode == Mode_Multi_Tier_JIT) { + /* Notify backend threads to start llvm jit compilation */ + module->enable_llvm_jit_compilation = true; + + /* Free fast_jit_func_ptrs if it is allocated before */ + if (module_inst->fast_jit_func_ptrs + && module_inst->fast_jit_func_ptrs != module->fast_jit_func_ptrs) { + wasm_runtime_free(module_inst->fast_jit_func_ptrs); + } + module_inst->fast_jit_func_ptrs = module->fast_jit_func_ptrs; + + /* Copy all llvm jit func ptrs from the module */ + bh_memcpy_s(module_inst->func_ptrs + module->import_function_count, + sizeof(void *) * module->function_count, module->func_ptrs, + sizeof(void *) * module->function_count); + } + else { + bh_assert(0); + } + + /* Add module instance into module's instance list if not added */ + if (first_time_set) { + bool found = false; + WASMModuleInstance *node = module->instance_list; + + while (node) { + if (node == module_inst) { + found = true; + break; + } + node = node->e->next; + } + + if (!found) { + module_inst->e->next = module->instance_list; + module->instance_list = module_inst; + } + } + + os_mutex_unlock(&module->instance_list_lock); +#endif /* end of !(WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ + && WASM_ENABLE_LAZY_JIT != 0) */ + + (void)module; + return true; +} + +bool +wasm_set_running_mode(WASMModuleInstance *module_inst, RunningMode running_mode) +{ + return set_running_mode(module_inst, running_mode, false); +} + /** * Instantiate module */ @@ -1419,15 +1637,6 @@ wasm_instantiate(WASMModule *module, bool is_sub_inst, uint32 stack_size, module_inst->e = (WASMModuleInstanceExtra *)((uint8 *)module_inst + extra_info_offset); -#if WASM_ENABLE_SHARED_MEMORY != 0 - if (os_mutex_init(&module_inst->e->mem_lock) != 0) { - set_error_buf(error_buf, error_buf_size, - "create shared memory lock failed"); - goto fail; - } - module_inst->e->mem_lock_inited = true; -#endif - #if WASM_ENABLE_MULTI_MODULE != 0 module_inst->e->sub_module_inst_list = &module_inst->e->sub_module_inst_list_head; @@ -1800,33 +2009,39 @@ wasm_instantiate(WASMModule *module, bool is_sub_inst, uint32 stack_size, } #endif -#if WASM_ENABLE_DEBUG_INTERP != 0 \ - || (WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ - && WASM_ENABLE_LAZY_JIT != 0) +#if WASM_ENABLE_WASI_NN != 0 + if (!is_sub_inst) { + if (!(module_inst->e->wasi_nn_ctx = wasi_nn_initialize())) { + set_error_buf(error_buf, error_buf_size, + "wasi nn initialization failed"); + goto fail; + } + } +#endif + +#if WASM_ENABLE_DEBUG_INTERP != 0 if (!is_sub_inst) { /* Add module instance into module's instance list */ os_mutex_lock(&module->instance_list_lock); -#if WASM_ENABLE_DEBUG_INTERP != 0 if (module->instance_list) { LOG_WARNING( "warning: multiple instances referencing to the same module " "may cause unexpected behaviour during debugging"); } -#endif -#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ - && WASM_ENABLE_LAZY_JIT != 0 - /* Copy llvm func ptrs again in case that they were updated - after the module instance was created */ - bh_memcpy_s(module_inst->func_ptrs + module->import_function_count, - sizeof(void *) * module->function_count, module->func_ptrs, - sizeof(void *) * module->function_count); -#endif module_inst->e->next = module->instance_list; module->instance_list = module_inst; os_mutex_unlock(&module->instance_list_lock); } #endif + /* Set running mode before executing wasm functions */ + if (!set_running_mode(module_inst, wasm_runtime_get_default_running_mode(), + true)) { + set_error_buf(error_buf, error_buf_size, + "set instance running mode failed"); + goto fail; + } + if (module->start_function != (uint32)-1) { /* TODO: fix start function can be import function issue */ if (module->start_function >= module->import_function_count) @@ -1834,45 +2049,11 @@ wasm_instantiate(WASMModule *module, bool is_sub_inst, uint32 stack_size, &module_inst->e->functions[module->start_function]; } - if ( -#if WASM_ENABLE_LIBC_WASI != 0 - /* - * reactor instances may assume that _initialize will be called by - * the environment at most once, and that none of their other - * exports are accessed before that call. - * - * let the loader decide how to act if there is no _initialize - * in a reactor - */ - !execute_initialize_function(module_inst) || -#endif - /* Execute __post_instantiate function */ - !execute_post_inst_function(module_inst) - /* Execute the function in "start" section */ - || !execute_start_function(module_inst)) { + if (!execute_post_instantiate_functions(module_inst, is_sub_inst)) { set_error_buf(error_buf, error_buf_size, module_inst->cur_exception); goto fail; } -#if WASM_ENABLE_BULK_MEMORY != 0 -#if WASM_ENABLE_LIBC_WASI != 0 - if (!module->import_wasi_api) { -#endif - /* Only execute the memory init function for main instance because - the data segments will be dropped once initialized. - */ - if (!is_sub_inst) { - if (!execute_memory_init_function(module_inst)) { - set_error_buf(error_buf, error_buf_size, - module_inst->cur_exception); - goto fail; - } - } -#if WASM_ENABLE_LIBC_WASI != 0 - } -#endif -#endif - #if WASM_ENABLE_MEMORY_TRACING != 0 wasm_runtime_dump_module_inst_mem_consumption( (WASMModuleInstanceCommon *)module_inst); @@ -1892,11 +2073,57 @@ wasm_deinstantiate(WASMModuleInstance *module_inst, bool is_sub_inst) if (!module_inst) return; + if (module_inst->exec_env_singleton) { + /* wasm_exec_env_destroy will call + wasm_cluster_wait_for_all_except_self to wait for other + threads, so as to destroy their exec_envs and module + instances first, and avoid accessing the shared resources + of current module instance after it is deinstantiated. */ + wasm_exec_env_destroy(module_inst->exec_env_singleton); + } + +#if WASM_ENABLE_DEBUG_INTERP != 0 \ + || (WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ + && WASM_ENABLE_LAZY_JIT != 0) + /* Remove instance from module's instance list before freeing + func_ptrs and fast_jit_func_ptrs of the instance, to avoid + accessing the freed memory in the jit backend compilation + threads */ + if (!is_sub_inst) { + WASMModule *module = module_inst->module; + WASMModuleInstance *instance_prev = NULL, *instance; + os_mutex_lock(&module->instance_list_lock); + + instance = module->instance_list; + while (instance) { + if (instance == module_inst) { + if (!instance_prev) + module->instance_list = instance->e->next; + else + instance_prev->e->next = instance->e->next; + break; + } + instance_prev = instance; + instance = instance->e->next; + } + + os_mutex_unlock(&module->instance_list_lock); + } +#endif + #if WASM_ENABLE_JIT != 0 if (module_inst->func_ptrs) wasm_runtime_free(module_inst->func_ptrs); #endif +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ + && WASM_ENABLE_LAZY_JIT != 0 + if (module_inst->fast_jit_func_ptrs + && module_inst->fast_jit_func_ptrs + != module_inst->module->fast_jit_func_ptrs) + wasm_runtime_free(module_inst->fast_jit_func_ptrs); +#endif + #if WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 if (module_inst->func_type_indexes) wasm_runtime_free(module_inst->func_type_indexes); @@ -1937,9 +2164,6 @@ wasm_deinstantiate(WASMModuleInstance *module_inst, bool is_sub_inst) wasm_externref_cleanup((WASMModuleInstanceCommon *)module_inst); #endif - if (module_inst->exec_env_singleton) - wasm_exec_env_destroy(module_inst->exec_env_singleton); - #if WASM_ENABLE_DUMP_CALL_STACK != 0 if (module_inst->frames) { bh_vector_destroy(module_inst->frames); @@ -1948,39 +2172,17 @@ wasm_deinstantiate(WASMModuleInstance *module_inst, bool is_sub_inst) } #endif -#if WASM_ENABLE_DEBUG_INTERP != 0 \ - || (WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ - && WASM_ENABLE_LAZY_JIT != 0) - if (!is_sub_inst) { - WASMModule *module = module_inst->module; - WASMModuleInstance *instance_prev = NULL, *instance; - os_mutex_lock(&module->instance_list_lock); - - instance = module->instance_list; - while (instance) { - if (instance == module_inst) { - if (!instance_prev) - module->instance_list = instance->e->next; - else - instance_prev->e->next = instance->e->next; - break; - } - instance_prev = instance; - instance = instance->e->next; - } - - os_mutex_unlock(&module->instance_list_lock); - } -#endif - -#if WASM_ENABLE_SHARED_MEMORY != 0 - if (module_inst->e->mem_lock_inited) - os_mutex_destroy(&module_inst->e->mem_lock); -#endif - if (module_inst->e->c_api_func_imports) wasm_runtime_free(module_inst->e->c_api_func_imports); +#if WASM_ENABLE_WASI_NN != 0 + if (!is_sub_inst) { + WASINNContext *wasi_nn_ctx = module_inst->e->wasi_nn_ctx; + if (wasi_nn_ctx) + wasi_nn_destroy(wasi_nn_ctx); + } +#endif + wasm_runtime_free(module_inst); } @@ -2030,24 +2232,6 @@ wasm_lookup_table(const WASMModuleInstance *module_inst, const char *name) } #endif -static bool -clear_wasi_proc_exit_exception(WASMModuleInstance *module_inst) -{ -#if WASM_ENABLE_LIBC_WASI != 0 - const char *exception = wasm_get_exception(module_inst); - if (exception && !strcmp(exception, "Exception: wasi proc exit")) { - /* The "wasi proc exit" exception is thrown by native lib to - let wasm app exit, which is a normal behavior, we clear - the exception here. */ - wasm_set_exception(module_inst, NULL); - return true; - } - return false; -#else - return false; -#endif -} - #ifdef OS_ENABLE_HW_BOUND_CHECK static void @@ -2063,14 +2247,16 @@ call_wasm_with_hw_bound_check(WASMModuleInstance *module_inst, WASMRuntimeFrame *prev_frame = wasm_exec_env_get_cur_frame(exec_env); uint8 *prev_top = exec_env->wasm_stack.s.top; #ifdef BH_PLATFORM_WINDOWS - const char *exce; int result; + bool has_exception; + char exception[EXCEPTION_BUF_LEN]; #endif bool ret = true; /* Check native stack overflow firstly to ensure we have enough native stack to run the following codes before actually calling the aot function in invokeNative function. */ + RECORD_STACK_USAGE(exec_env, (uint8 *)&exec_env_tls); if ((uint8 *)&exec_env_tls < exec_env->native_stack_boundary + page_size * (guard_page_count + 1)) { wasm_set_exception(module_inst, "native stack overflow"); @@ -2096,14 +2282,14 @@ call_wasm_with_hw_bound_check(WASMModuleInstance *module_inst, #else __try { wasm_interp_call_wasm(module_inst, exec_env, function, argc, argv); - } __except (wasm_get_exception(module_inst) + } __except (wasm_copy_exception(module_inst, NULL) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { /* exception was thrown in wasm_exception_handler */ ret = false; } - if ((exce = wasm_get_exception(module_inst)) - && strstr(exce, "native stack overflow")) { + has_exception = wasm_copy_exception(module_inst, exception); + if (has_exception && strstr(exception, "native stack overflow")) { /* After a stack overflow, the stack was left in a damaged state, let the CRT repair it */ result = _resetstkoflw(); @@ -2157,8 +2343,7 @@ wasm_call_function(WASMExecEnv *exec_env, WASMFunctionInstance *function, wasm_exec_env_set_thread_info(exec_env); interp_call_wasm(module_inst, exec_env, function, argc, argv); - (void)clear_wasi_proc_exit_exception(module_inst); - return !wasm_get_exception(module_inst) ? true : false; + return !wasm_copy_exception(module_inst, NULL); } bool @@ -2185,7 +2370,7 @@ wasm_create_exec_env_and_call_function(WASMModuleInstance *module_inst, } } - ret = wasm_call_function(exec_env, func, argc, argv); + ret = wasm_runtime_call_wasm(exec_env, func, argc, argv); /* don't destroy the exec_env if it isn't created in this function */ if (!existing_exec_env) @@ -2331,6 +2516,12 @@ wasm_module_free(WASMModuleInstance *module_inst, uint32 ptr) return; } +#if WASM_ENABLE_SHARED_MEMORY != 0 + WASMSharedMemNode *node = wasm_module_get_shared_memory( + (WASMModuleCommon *)module_inst->module); + if (node) + os_mutex_lock(&node->shared_mem_lock); +#endif addr = memory->memory_data + ptr; if (memory->heap_handle && memory->heap_data <= addr @@ -2343,6 +2534,10 @@ wasm_module_free(WASMModuleInstance *module_inst, uint32 ptr) execute_free_function(module_inst, module_inst->e->free_function, ptr); } +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (node) + os_mutex_unlock(&node->shared_mem_lock); +#endif } } @@ -2455,8 +2650,7 @@ call_indirect(WASMExecEnv *exec_env, uint32 tbl_idx, uint32 elem_idx, interp_call_wasm(module_inst, exec_env, func_inst, argc, argv); - (void)clear_wasi_proc_exit_exception(module_inst); - return !wasm_get_exception(module_inst) ? true : false; + return !wasm_copy_exception(module_inst, NULL); got_exception: return false; @@ -2476,14 +2670,16 @@ wasm_set_aux_stack(WASMExecEnv *exec_env, uint32 start_offset, uint32 size) WASMModuleInstance *module_inst = (WASMModuleInstance *)exec_env->module_inst; uint32 stack_top_idx = module_inst->module->aux_stack_top_global_index; + +#if WASM_ENABLE_HEAP_AUX_STACK_ALLOCATION == 0 + /* Check the aux stack space */ uint32 data_end = module_inst->module->aux_data_end; uint32 stack_bottom = module_inst->module->aux_stack_bottom; bool is_stack_before_data = stack_bottom < data_end ? true : false; - - /* Check the aux stack space, currently we don't allocate space in heap */ if ((is_stack_before_data && (size > start_offset)) || ((!is_stack_before_data) && (start_offset - data_end < size))) return false; +#endif if (stack_top_idx != (uint32)-1) { /* The aux stack top is a wasm global, @@ -2905,8 +3101,14 @@ llvm_jit_invoke_native(WASMExecEnv *exec_env, uint32 func_idx, uint32 argc, import_func = &module->import_functions[func_idx].u.function; if (import_func->call_conv_wasm_c_api) { - c_api_func_import = module_inst->e->c_api_func_imports + func_idx; - func_ptr = c_api_func_import->func_ptr_linked; + if (module_inst->e->c_api_func_imports) { + c_api_func_import = module_inst->e->c_api_func_imports + func_idx; + func_ptr = c_api_func_import->func_ptr_linked; + } + else { + c_api_func_import = NULL; + func_ptr = NULL; + } } if (!func_ptr) { diff --git a/core/iwasm/interpreter/wasm_runtime.h b/core/iwasm/interpreter/wasm_runtime.h index 5bf09bb81..1767c540a 100644 --- a/core/iwasm/interpreter/wasm_runtime.h +++ b/core/iwasm/interpreter/wasm_runtime.h @@ -11,10 +11,16 @@ #include "../common/wasm_runtime_common.h" #include "../common/wasm_exec_env.h" +#if WASM_ENABLE_WASI_NN != 0 +#include "../libraries/wasi-nn/src/wasi_nn_private.h" +#endif + #ifdef __cplusplus extern "C" { #endif +#define EXCEPTION_BUF_LEN 128 + typedef struct WASMModuleInstance WASMModuleInstance; typedef struct WASMFunctionInstance WASMFunctionInstance; typedef struct WASMMemoryInstance WASMMemoryInstance; @@ -59,9 +65,7 @@ typedef enum WASMExceptionID { EXCE_AUX_STACK_UNDERFLOW, EXCE_OUT_OF_BOUNDS_TABLE_ACCESS, EXCE_OPERAND_STACK_OVERFLOW, -#if WASM_ENABLE_FAST_JIT != 0 EXCE_FAILED_TO_COMPILE_FAST_JIT_FUNC, -#endif EXCE_ALREADY_THROWN, EXCE_NUM, } WASMExceptionID; @@ -219,12 +223,7 @@ typedef struct WASMModuleInstanceExtra { WASMFunctionInstance *retain_function; CApiFuncImport *c_api_func_imports; - -#if WASM_ENABLE_SHARED_MEMORY != 0 - /* lock for shared memory atomic operations */ - korp_mutex mem_lock; - bool mem_lock_inited; -#endif + RunningMode running_mode; #if WASM_ENABLE_MULTI_MODULE != 0 bh_list sub_module_inst_list_head; @@ -237,11 +236,15 @@ typedef struct WASMModuleInstanceExtra { uint32 max_aux_stack_used; #endif -#if WASM_ENABLE_DEBUG_INTERP != 0 \ - || (WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT \ +#if WASM_ENABLE_DEBUG_INTERP != 0 \ + || (WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ && WASM_ENABLE_LAZY_JIT != 0) WASMModuleInstance *next; #endif + +#if WASM_ENABLE_WASI_NN != 0 + WASINNContext *wasi_nn_ctx; +#endif } WASMModuleInstanceExtra; struct AOTFuncPerfProfInfo; @@ -281,7 +284,7 @@ struct WASMModuleInstance { DefPointer(WASMExportTabInstance *, export_tables); /* The exception buffer of wasm interpreter for current thread. */ - char cur_exception[128]; + char cur_exception[EXCEPTION_BUF_LEN]; /* The WASM module or AOT module, for AOTModuleInstance, it denotes `AOTModule *` */ @@ -298,7 +301,11 @@ struct WASMModuleInstance { not available in AOTModuleInstance */ DefPointer(void **, import_func_ptrs); /* Array of function pointers to fast jit functions, - not available in AOTModuleInstance */ + not available in AOTModuleInstance: + Only when the multi-tier JIT macros are all enabled and the running + mode of current module instance is set to Mode_Fast_JIT, runtime + will allocate new memory for it, otherwise it always points to the + module->fast_jit_func_ptrs */ DefPointer(void **, fast_jit_func_ptrs); /* The custom data that can be set/get by wasm_{get|set}_custom_data */ DefPointer(void *, custom_data); @@ -402,6 +409,10 @@ wasm_dump_perf_profiling(const WASMModuleInstance *module_inst); void wasm_deinstantiate(WASMModuleInstance *module_inst, bool is_sub_inst); +bool +wasm_set_running_mode(WASMModuleInstance *module_inst, + RunningMode running_mode); + WASMFunctionInstance * wasm_lookup_function(const WASMModuleInstance *module_inst, const char *name, const char *signature); @@ -435,6 +446,15 @@ wasm_set_exception_with_id(WASMModuleInstance *module_inst, uint32 id); const char * wasm_get_exception(WASMModuleInstance *module); +/** + * @brief Copy exception in buffer passed as parameter. Thread-safe version of + * `wasm_get_exception()` + * @note Buffer size must be no smaller than EXCEPTION_BUF_LEN + * @return true if exception found + */ +bool +wasm_copy_exception(WASMModuleInstance *module_inst, char *exception_buf); + uint32 wasm_module_malloc(WASMModuleInstance *module_inst, uint32 size, void **p_native_addr); diff --git a/core/iwasm/libraries/lib-pthread/lib_pthread_wrapper.c b/core/iwasm/libraries/lib-pthread/lib_pthread_wrapper.c index 2fb7033f3..d2ca601d9 100644 --- a/core/iwasm/libraries/lib-pthread/lib_pthread_wrapper.c +++ b/core/iwasm/libraries/lib-pthread/lib_pthread_wrapper.c @@ -46,10 +46,6 @@ wasm_runtime_addr_native_to_app(module_inst, ptr) /* clang-format on */ -extern bool -wasm_runtime_call_indirect(wasm_exec_env_t exec_env, uint32 element_indices, - uint32 argc, uint32 argv[]); - enum { T_THREAD, T_MUTEX, @@ -494,7 +490,6 @@ pthread_start_routine(void *arg) { wasm_exec_env_t exec_env = (wasm_exec_env_t)arg; wasm_exec_env_t parent_exec_env; - wasm_module_inst_t module_inst = get_module_inst(exec_env); ThreadRoutineArgs *routine_args = exec_env->thread_arg; ThreadInfoNode *info_node = routine_args->info_node; uint32 argv[1]; @@ -504,7 +499,6 @@ pthread_start_routine(void *arg) info_node->exec_env = exec_env; info_node->u.thread = exec_env->handle; if (!append_thread_info_node(info_node)) { - wasm_runtime_deinstantiate_internal(module_inst, true); delete_thread_info_node(info_node); os_cond_signal(&parent_exec_env->wait_cond); os_mutex_unlock(&parent_exec_env->wait_lock); @@ -520,16 +514,12 @@ pthread_start_routine(void *arg) if (!wasm_runtime_call_indirect(exec_env, routine_args->elem_index, 1, argv)) { - if (wasm_runtime_get_exception(module_inst)) - wasm_cluster_spread_exception(exec_env); + /* Exception has already been spread during throwing */ } /* destroy pthread key values */ call_key_destructor(exec_env); - /* routine exit, destroy instance */ - wasm_runtime_deinstantiate_internal(module_inst, true); - wasm_runtime_free(routine_args); /* if the thread is joinable, store the result in its info node, @@ -571,6 +561,7 @@ pthread_create_wrapper(wasm_exec_env_t exec_env, #if WASM_ENABLE_LIBC_WASI != 0 WASIContext *wasi_ctx; #endif + CApiFuncImport **new_c_api_func_imports = NULL; bh_assert(module); bh_assert(module_inst); @@ -603,6 +594,46 @@ pthread_create_wrapper(wasm_exec_env_t exec_env, wasm_runtime_set_wasi_ctx(new_module_inst, wasi_ctx); #endif + /* workaround about passing instantiate-linking information */ + { + CApiFuncImport *c_api_func_imports; + uint32 import_func_count = 0; + uint32 size_in_bytes = 0; + +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + new_c_api_func_imports = &( + ((WASMModuleInstance *)new_module_inst)->e->c_api_func_imports); + c_api_func_imports = + ((WASMModuleInstance *)module_inst)->e->c_api_func_imports; + import_func_count = ((WASMModule *)module)->import_function_count; + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + AOTModuleInstanceExtra *e = + (AOTModuleInstanceExtra *)((AOTModuleInstance *)new_module_inst) + ->e; + new_c_api_func_imports = &(e->c_api_func_imports); + + e = (AOTModuleInstanceExtra *)((AOTModuleInstance *)module_inst)->e; + c_api_func_imports = e->c_api_func_imports; + + import_func_count = ((AOTModule *)module)->import_func_count; + } +#endif + + if (import_func_count != 0 && c_api_func_imports) { + size_in_bytes = sizeof(CApiFuncImport *) * import_func_count; + *new_c_api_func_imports = wasm_runtime_malloc(size_in_bytes); + if (!(*new_c_api_func_imports)) + goto fail; + + bh_memcpy_s(*new_c_api_func_imports, size_in_bytes, + c_api_func_imports, size_in_bytes); + } + } + if (!(info_node = wasm_runtime_malloc(sizeof(ThreadInfoNode)))) goto fail; @@ -623,8 +654,9 @@ pthread_create_wrapper(wasm_exec_env_t exec_env, routine_args->module_inst = new_module_inst; os_mutex_lock(&exec_env->wait_lock); - ret = wasm_cluster_create_thread( - exec_env, new_module_inst, pthread_start_routine, (void *)routine_args); + ret = + wasm_cluster_create_thread(exec_env, new_module_inst, true, + pthread_start_routine, (void *)routine_args); if (ret != 0) { os_mutex_unlock(&exec_env->wait_lock); goto fail; diff --git a/core/iwasm/libraries/lib-socket/test/build.sh b/core/iwasm/libraries/lib-socket/test/build.sh new file mode 100755 index 000000000..ec8d6608c --- /dev/null +++ b/core/iwasm/libraries/lib-socket/test/build.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set -ueo pipefail +CC="${CC:=/opt/wasi-sdk/bin/clang}" +files=("tcp_udp.c" "nslookup.c") +WASI_SYSROOT=${WASI_SYSROOT:=~/dev/wasi-libc/sysroot} + +for file in "${files[@]}" +do + echo $file + $CC \ + --target=wasm32-wasi-threads \ + -I../inc \ + --sysroot $WASI_SYSROOT \ + ../src/wasi/wasi_socket_ext.c -pthread -ftls-model=local-exec \ + -Wl,--allow-undefined \ + -Wl,--strip-all,--no-entry \ + -Wl,--export=__heap_base \ + -Wl,--export=__data_end \ + -Wl,--shared-memory,--max-memory=10485760 \ + -Wl,--export=malloc \ + -Wl,--export=free \ + -o "${file%.*}.wasm" "$file" +done \ No newline at end of file diff --git a/core/iwasm/libraries/lib-socket/test/nslookup.c b/core/iwasm/libraries/lib-socket/test/nslookup.c new file mode 100644 index 000000000..37150f1eb --- /dev/null +++ b/core/iwasm/libraries/lib-socket/test/nslookup.c @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#ifdef __wasi__ +#include +#include +#include +#include +#else +#include +#endif + +void +test_nslookup(int af) +{ + struct addrinfo *res; + int count = 0; + struct addrinfo hints; + char *url = "google-public-dns-a.google.com"; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = af; + hints.ai_socktype = SOCK_STREAM; + int ret = getaddrinfo(url, 0, &hints, &res); + assert(ret == 0); + struct addrinfo *address = res; + while (address) { + assert(address->ai_family == af); + assert(address->ai_socktype == SOCK_STREAM); + count++; + address = address->ai_next; + } + + assert(count > 0); + freeaddrinfo(res); +} + +int +main() +{ + test_nslookup(AF_INET); /* for ipv4 */ + test_nslookup(AF_INET6); /* for ipv6 */ + + return 0; +} diff --git a/core/iwasm/libraries/lib-socket/test/tcp_udp.c b/core/iwasm/libraries/lib-socket/test/tcp_udp.c new file mode 100644 index 000000000..49231de89 --- /dev/null +++ b/core/iwasm/libraries/lib-socket/test/tcp_udp.c @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +#include +#include +#include +#ifdef __wasi__ +#include +#include +#include +#endif +#include +#include +#define SERVER_MSG "Message from server." +#define PORT 8989 +pthread_mutex_t mut; +pthread_cond_t cond; +int server_init_complete = 0; +char buffer[sizeof(SERVER_MSG) + 1]; + +struct socket_info { + union { + struct sockaddr_in addr_ipv4; + struct sockaddr_in6 addr_ipv6; + } addr; + int sock; +}; + +struct thread_args { + int family; + int protocol; +}; + +struct socket_info +init_socket_addr(int family, int protocol) +{ + int sock = socket(family, protocol, 0); + assert(sock != -1); + + struct socket_info info; + if (family == AF_INET) { + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(PORT); + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + info.addr.addr_ipv4 = addr; + } + else if (family == AF_INET6) { + struct sockaddr_in6 addr; + memset(&addr, 0, sizeof(addr)); + addr.sin6_family = AF_INET6; + addr.sin6_port = htons(PORT); + addr.sin6_addr = in6addr_loopback; + info.addr.addr_ipv6 = addr; + } + info.sock = sock; + return info; +} + +void +assert_thread_args(struct thread_args *args) +{ + assert(args->family == AF_INET || args->family == AF_INET6); + assert(args->protocol == SOCK_STREAM || args->protocol == SOCK_DGRAM); +} + +void * +server(void *arg) +{ + server_init_complete = 0; + struct thread_args *args = (struct thread_args *)arg; + assert_thread_args(args); + + struct socket_info init_server_sock = + init_socket_addr(args->family, args->protocol); + + int server_sock = init_server_sock.sock; + socklen_t addr_size; + struct sockaddr_storage client_addr; + strcpy(buffer, SERVER_MSG); + + struct sockaddr *server_addr = (struct sockaddr *)&init_server_sock.addr; + int ret = bind(server_sock, server_addr, + args->family == AF_INET ? sizeof(struct sockaddr_in) + : sizeof(struct sockaddr_in6)); + assert(ret == 0); + + (args->protocol == SOCK_STREAM) && listen(server_sock, 1); + pthread_mutex_lock(&mut); + server_init_complete = 1; + pthread_mutex_unlock(&mut); + pthread_cond_signal(&cond); + + addr_size = sizeof(client_addr); + if (args->protocol == SOCK_STREAM) { + int client_sock = + accept(server_sock, (struct sockaddr *)&client_addr, &addr_size); + assert(client_sock >= 0); + sendto(client_sock, buffer, strlen(buffer), 0, + (struct sockaddr *)&client_addr, addr_size); + + assert(close(client_sock) == 0); + } + else { + recvfrom(server_sock, buffer, sizeof(buffer), 0, + (struct sockaddr *)&client_addr, &addr_size); + sendto(server_sock, buffer, strlen(buffer), 0, + (struct sockaddr *)&client_addr, addr_size); + + assert(close(server_sock) == 0); + } + + return NULL; +} + +void * +client(void *arg) +{ + struct thread_args *args = (struct thread_args *)arg; + assert_thread_args(args); + + pthread_mutex_lock(&mut); + + while (server_init_complete == 0) { + pthread_cond_wait(&cond, &mut); + } + + struct socket_info init_client_sock = + init_socket_addr(args->family, args->protocol); + int sock = init_client_sock.sock; + pthread_mutex_unlock(&mut); + + if (args->family == AF_INET) { + struct sockaddr_in addr = init_client_sock.addr.addr_ipv4; + if (args->protocol == SOCK_STREAM) { + assert(connect(sock, (struct sockaddr *)&addr, sizeof(addr)) != -1); + } + else { + assert(sendto(sock, buffer, strlen(buffer), 0, + (struct sockaddr *)&addr, sizeof(addr)) + != -1); + } + } + else { + struct sockaddr_in6 addr = init_client_sock.addr.addr_ipv6; + if (args->protocol == SOCK_STREAM) { + assert(connect(sock, (struct sockaddr *)&addr, sizeof(addr)) != -1); + } + else { + assert(sendto(sock, buffer, strlen(buffer), 0, + (struct sockaddr *)&addr, sizeof(addr)) + != -1); + } + } + + recv(sock, buffer, sizeof(buffer), 0); + assert(strcmp(buffer, SERVER_MSG) == 0); + assert(close(sock) == 0); + return NULL; +} + +void +test_protocol(int family, int protocol) +{ + pthread_t server_thread, client_thread; + assert(pthread_cond_init(&cond, NULL) == 0); + assert(pthread_mutex_init(&mut, NULL) == 0); + + struct thread_args args = { family, protocol }; + assert(pthread_create(&server_thread, NULL, server, (void *)&args) == 0); + assert(pthread_create(&client_thread, NULL, client, (void *)&args) == 0); + assert(pthread_join(server_thread, NULL) == 0); + assert(pthread_join(client_thread, NULL) == 0); + + assert(pthread_mutex_destroy(&mut) == 0); + assert(pthread_cond_destroy(&cond) == 0); +} + +int +main(int argc, char **argv) +{ + /* test tcp with ipv4 and ipv6 */ + test_protocol(AF_INET, SOCK_STREAM); + test_protocol(AF_INET6, SOCK_STREAM); + + /* test udp with ipv4 and ipv6 */ + test_protocol(AF_INET, SOCK_DGRAM); + test_protocol(AF_INET6, SOCK_DGRAM); + + return 0; +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads.cmake b/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads.cmake new file mode 100644 index 000000000..54d2ba902 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads.cmake @@ -0,0 +1,12 @@ +# Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (LIB_WASI_THREADS_DIR ${CMAKE_CURRENT_LIST_DIR}) + +add_definitions (-DWASM_ENABLE_LIB_WASI_THREADS=1 -DWASM_ENABLE_HEAP_AUX_STACK_ALLOCATION=1) + +include_directories(${LIB_WASI_THREADS_DIR}) + +set (LIB_WASI_THREADS_SOURCE + ${LIB_WASI_THREADS_DIR}/lib_wasi_threads_wrapper.c + ${LIB_WASI_THREADS_DIR}/tid_allocator.c) \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c b/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c new file mode 100644 index 000000000..db96898db --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "bh_log.h" +#include "thread_manager.h" +#include "tid_allocator.h" + +#if WASM_ENABLE_INTERP != 0 +#include "wasm_runtime.h" +#endif + +#if WASM_ENABLE_AOT != 0 +#include "aot_runtime.h" +#endif + +static const char *THREAD_START_FUNCTION = "wasi_thread_start"; +static korp_mutex thread_id_lock; +static TidAllocator tid_allocator; + +typedef struct { + /* app's entry function */ + wasm_function_inst_t start_func; + /* arg of the app's entry function */ + uint32 arg; + /* thread id passed to the app */ + int32 thread_id; +} ThreadStartArg; + +static int32 +allocate_thread_id() +{ + os_mutex_lock(&thread_id_lock); + int32 id = tid_allocator_get_tid(&tid_allocator); + os_mutex_unlock(&thread_id_lock); + + return id; +} + +void +deallocate_thread_id(int32 thread_id) +{ + os_mutex_lock(&thread_id_lock); + tid_allocator_release_tid(&tid_allocator, thread_id); + os_mutex_unlock(&thread_id_lock); +} + +static void * +thread_start(void *arg) +{ + wasm_exec_env_t exec_env = (wasm_exec_env_t)arg; + ThreadStartArg *thread_arg = exec_env->thread_arg; + uint32 argv[2]; + + wasm_exec_env_set_thread_info(exec_env); + argv[0] = thread_arg->thread_id; + argv[1] = thread_arg->arg; + + if (!wasm_runtime_call_wasm(exec_env, thread_arg->start_func, 2, argv)) { + /* Exception has already been spread during throwing */ + } + + // Routine exit + deallocate_thread_id(thread_arg->thread_id); + wasm_runtime_free(thread_arg); + exec_env->thread_arg = NULL; + + return NULL; +} + +static int32 +thread_spawn_wrapper(wasm_exec_env_t exec_env, uint32 start_arg) +{ + wasm_module_t module = wasm_exec_env_get_module(exec_env); + wasm_module_inst_t module_inst = get_module_inst(exec_env); + wasm_module_inst_t new_module_inst = NULL; + ThreadStartArg *thread_start_arg = NULL; + wasm_function_inst_t start_func; + int32 thread_id; + uint32 stack_size = 8192; + int32 ret = -1; +#if WASM_ENABLE_LIBC_WASI != 0 + WASIContext *wasi_ctx; +#endif + + bh_assert(module); + bh_assert(module_inst); + + stack_size = ((WASMModuleInstance *)module_inst)->default_wasm_stack_size; + + if (!(new_module_inst = wasm_runtime_instantiate_internal( + module, true, stack_size, 0, NULL, 0))) + return -1; + + wasm_runtime_set_custom_data_internal( + new_module_inst, wasm_runtime_get_custom_data(module_inst)); + +#if WASM_ENABLE_LIBC_WASI != 0 + wasi_ctx = wasm_runtime_get_wasi_ctx(module_inst); + if (wasi_ctx) + wasm_runtime_set_wasi_ctx(new_module_inst, wasi_ctx); +#endif + + start_func = wasm_runtime_lookup_function(new_module_inst, + THREAD_START_FUNCTION, NULL); + if (!start_func) { + LOG_ERROR("Failed to find thread start function %s", + THREAD_START_FUNCTION); + goto thread_preparation_fail; + } + + if (!(thread_start_arg = wasm_runtime_malloc(sizeof(ThreadStartArg)))) { + LOG_ERROR("Runtime args allocation failed"); + goto thread_preparation_fail; + } + + thread_start_arg->thread_id = thread_id = allocate_thread_id(); + if (thread_id < 0) { + LOG_ERROR("Failed to get thread identifier"); + goto thread_preparation_fail; + } + thread_start_arg->arg = start_arg; + thread_start_arg->start_func = start_func; + + ret = wasm_cluster_create_thread(exec_env, new_module_inst, false, + thread_start, thread_start_arg); + if (ret != 0) { + LOG_ERROR("Failed to spawn a new thread"); + goto thread_spawn_fail; + } + + return thread_id; + +thread_spawn_fail: + deallocate_thread_id(thread_id); + +thread_preparation_fail: + if (new_module_inst) + wasm_runtime_deinstantiate_internal(new_module_inst, true); + if (thread_start_arg) + wasm_runtime_free(thread_start_arg); + + return -1; +} + +/* clang-format off */ +#define REG_NATIVE_FUNC(name, func_name, signature) \ + { name, func_name##_wrapper, signature, NULL } +/* clang-format on */ + +static NativeSymbol native_symbols_lib_wasi_threads[] = { REG_NATIVE_FUNC( + "thread-spawn", thread_spawn, "(i)i") }; + +uint32 +get_lib_wasi_threads_export_apis(NativeSymbol **p_lib_wasi_threads_apis) +{ + *p_lib_wasi_threads_apis = native_symbols_lib_wasi_threads; + return sizeof(native_symbols_lib_wasi_threads) / sizeof(NativeSymbol); +} + +bool +lib_wasi_threads_init(void) +{ + if (0 != os_mutex_init(&thread_id_lock)) + return false; + + if (!tid_allocator_init(&tid_allocator)) { + os_mutex_destroy(&thread_id_lock); + return false; + } + + return true; +} + +void +lib_wasi_threads_destroy(void) +{ + tid_allocator_deinit(&tid_allocator); + os_mutex_destroy(&thread_id_lock); +} diff --git a/core/iwasm/libraries/lib-wasi-threads/test/build.sh b/core/iwasm/libraries/lib-wasi-threads/test/build.sh new file mode 100644 index 000000000..58ff59481 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/build.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# +# Copyright (C) 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +CC=${CC:=/opt/wasi-sdk/bin/clang} +WASI_SYSROOT=${WASI_SYSROOT:=~/dev/wasi-libc/sysroot} +WAMR_DIR=../../../../.. + +for test_c in *.c; do + test_wasm="$(basename $test_c .c).wasm" + + echo "Compiling $test_c to $test_wasm" + $CC \ + --sysroot $WASI_SYSROOT \ + -target wasm32-wasi-threads \ + -pthread -ftls-model=local-exec \ + -z stack-size=32768 \ + -Wl,--export=__heap_base \ + -Wl,--export=__data_end \ + -Wl,--shared-memory,--max-memory=1966080 \ + -Wl,--export=wasi_thread_start \ + -Wl,--export=malloc \ + -Wl,--export=free \ + -I $WAMR_DIR/samples/wasi-threads/wasm-apps \ + $WAMR_DIR/samples/wasi-threads/wasm-apps/wasi_thread_start.S \ + $test_c -o $test_wasm +done \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/common.h b/core/iwasm/libraries/lib-wasi-threads/test/common.h new file mode 100644 index 000000000..a531e39dc --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/common.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2022 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include +#include +#include +#include + +#include "wasi_thread_start.h" + +typedef enum { + BLOCKING_TASK_BUSY_WAIT, + BLOCKING_TASK_ATOMIC_WAIT, + BLOCKING_TASK_POLL_ONEOFF +} blocking_task_type_t; + +/* Parameter to change test behavior */ +static bool termination_by_trap; +static bool termination_in_main_thread; +static blocking_task_type_t blocking_task_type; + +#define TIMEOUT_SECONDS 10ll +#define NUM_THREADS 3 +static pthread_barrier_t barrier; + +typedef struct { + start_args_t base; + bool throw_exception; +} shared_t; + +void +run_long_task() +{ + if (blocking_task_type == BLOCKING_TASK_BUSY_WAIT) { + for (int i = 0; i < TIMEOUT_SECONDS; i++) + sleep(1); + } + else if (blocking_task_type == BLOCKING_TASK_ATOMIC_WAIT) { + __builtin_wasm_memory_atomic_wait32( + 0, 0, TIMEOUT_SECONDS * 1000 * 1000 * 1000); + } + else { + sleep(TIMEOUT_SECONDS); + } +} + +void +start_job() +{ + /* Wait for all threads (including the main thread) to be ready */ + pthread_barrier_wait(&barrier); + run_long_task(); /* Task to be interrupted */ + assert(false && "Thread termination test failed"); +} + +void +terminate_process() +{ + /* Wait for all threads (including the main thread) to be ready */ + pthread_barrier_wait(&barrier); + + if (termination_by_trap) + __builtin_trap(); + else + __wasi_proc_exit(33); +} + +void +__wasi_thread_start_C(int thread_id, int *start_arg) +{ + shared_t *data = (shared_t *)start_arg; + + if (data->throw_exception) { + terminate_process(); + } + else { + start_job(); + } +} + +void +test_termination(bool trap, bool main, blocking_task_type_t task_type) +{ + termination_by_trap = trap; + termination_in_main_thread = main; + blocking_task_type = task_type; + + int thread_id = -1, i; + shared_t data[NUM_THREADS] = { 0 }; + assert(pthread_barrier_init(&barrier, NULL, NUM_THREADS + 1) == 0 + && "Failed to init barrier"); + + for (i = 0; i < NUM_THREADS; i++) { + /* No graceful memory free to simplify the test */ + assert(start_args_init(&data[i].base) + && "Failed to allocate thread's stack"); + } + + /* Create a thread that forces termination through trap or `proc_exit` */ + data[0].throw_exception = !termination_in_main_thread; + thread_id = __wasi_thread_spawn(&data[0]); + assert(thread_id > 0 && "Failed to create thread"); + + /* Create two additional threads to test exception propagation */ + data[1].throw_exception = false; + thread_id = __wasi_thread_spawn(&data[1]); + assert(thread_id > 0 && "Failed to create thread"); + data[2].throw_exception = false; + thread_id = __wasi_thread_spawn(&data[2]); + assert(thread_id > 0 && "Failed to create thread"); + + if (termination_in_main_thread) { + terminate_process(); + } + else { + start_job(); + } +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/create_threads_until_limit.c b/core/iwasm/libraries/lib-wasi-threads/test/create_threads_until_limit.c new file mode 100644 index 000000000..23ba5f627 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/create_threads_until_limit.c @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef __wasi__ +#error This example only compiles to WASM/WASI target +#endif + +#include +#include +#include +#include + +#include "wasi_thread_start.h" + +enum CONSTANTS { + MAX_NUM_THREADS = 4, /* Should be the same as "--max-threads" */ + NUM_RETRY = 5, + SECOND = 1000 * 1000 * 1000, /* 1 second */ + TIMEOUT = 10LL * SECOND +}; + +int g_count = 0; + +typedef struct { + start_args_t base; + int th_ready; + int th_continue; + int th_done; + bool no_ops; +} shared_t; + +void +__wasi_thread_start_C(int thread_id, int *start_arg) +{ + shared_t *data = (shared_t *)start_arg; + + if (data->no_ops) { + __builtin_wasm_memory_atomic_wait32(NULL, 0, 2 * SECOND); + return; + } + + __atomic_store_n(&data->th_ready, 1, __ATOMIC_SEQ_CST); + __builtin_wasm_memory_atomic_notify(&data->th_ready, 1); + + if (__builtin_wasm_memory_atomic_wait32(&data->th_continue, 0, TIMEOUT) + == 2) { + assert(false && "Wait should not time out"); + } + + __atomic_fetch_add(&g_count, 1, __ATOMIC_SEQ_CST); + + __atomic_store_n(&data->th_done, 1, __ATOMIC_SEQ_CST); + __builtin_wasm_memory_atomic_notify(&data->th_done, 1); +} + +int +main(int argc, char **argv) +{ + shared_t data[MAX_NUM_THREADS] = { 0 }; + int thread_ids[MAX_NUM_THREADS]; + + for (int i = 0; i < MAX_NUM_THREADS; i++) { + assert(start_args_init(&data[i].base)); + thread_ids[i] = __wasi_thread_spawn(&data[i]); + printf("Thread created with id=%d\n", thread_ids[i]); + assert(thread_ids[i] > 0 && "Thread creation failed"); + + for (int j = 0; j < i; j++) { + assert(thread_ids[i] != thread_ids[j] && "Duplicated TIDs"); + } + + if (__builtin_wasm_memory_atomic_wait32(&data[i].th_ready, 0, TIMEOUT) + == 2) { + assert(false && "Wait should not time out"); + } + } + + printf("Attempt to create thread when not possible\n"); + shared_t data_fail = { 0 }; + assert(start_args_init(&data_fail.base)); + int thread_id = __wasi_thread_spawn(&data_fail); + start_args_deinit(&data_fail.base); + assert(thread_id < 0 && "Thread creation should fail"); + + printf("Unlock created threads\n"); + for (int i = 0; i < MAX_NUM_THREADS; i++) { + __atomic_store_n(&data[i].th_continue, 1, __ATOMIC_SEQ_CST); + __builtin_wasm_memory_atomic_notify(&data[i].th_continue, 1); + } + + printf("Wait for threads to finish\n"); + for (int i = 0; i < MAX_NUM_THREADS; i++) { + if (__builtin_wasm_memory_atomic_wait32(&data[i].th_done, 0, TIMEOUT) + == 2) { + assert(false && "Wait should not time out"); + } + + start_args_deinit(&data[i].base); + } + + printf("Value of count after update: %d\n", g_count); + assert(g_count == (MAX_NUM_THREADS) + && "Global count not updated correctly"); + + /* --------------------------------------------------- */ + + printf("Create new threads without waiting from them to finish\n"); + shared_t data_no_join[MAX_NUM_THREADS] = { 0 }; + for (int i = 0; i < MAX_NUM_THREADS; i++) { + /* No graceful memory free to simplify the test */ + assert(start_args_init(&data_no_join[i].base)); + data_no_join[i].no_ops = true; + + int thread_id = -1; + for (int j = 0; j < NUM_RETRY && thread_id < 0; j++) { + thread_id = __wasi_thread_spawn(&data_no_join[i]); + if (thread_id < 0) + __builtin_wasm_memory_atomic_wait32(NULL, 0, SECOND); + } + + printf("Thread created with id=%d\n", thread_id); + assert(thread_id > 0 && "Thread creation should succeed"); + } + + return EXIT_SUCCESS; +} diff --git a/core/iwasm/libraries/lib-wasi-threads/test/global_atomic.c b/core/iwasm/libraries/lib-wasi-threads/test/global_atomic.c new file mode 100644 index 000000000..a38e75364 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/global_atomic.c @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef __wasi__ +#error This example only compiles to WASM/WASI target +#endif + +#include +#include +#include +#include + +#include "wasi_thread_start.h" + +enum CONSTANTS { + NUM_THREADS = 4, + NUM_ITER = 1000, + SECOND = 1000 * 1000 * 1000, /* 1 second */ + TIMEOUT = 10LL * SECOND +}; + +int g_count = 0; + +typedef struct { + start_args_t base; + int th_done; +} shared_t; + +void +__wasi_thread_start_C(int thread_id, int *start_arg) +{ + shared_t *data = (shared_t *)start_arg; + + for (int i = 0; i < NUM_ITER; i++) + __atomic_fetch_add(&g_count, 1, __ATOMIC_SEQ_CST); + + __atomic_store_n(&data->th_done, 1, __ATOMIC_SEQ_CST); + __builtin_wasm_memory_atomic_notify(&data->th_done, 1); +} + +int +main(int argc, char **argv) +{ + shared_t data[NUM_THREADS] = { 0 }; + int thread_ids[NUM_THREADS]; + + for (int i = 0; i < NUM_THREADS; i++) { + assert(start_args_init(&data[i].base)); + thread_ids[i] = __wasi_thread_spawn(&data[i]); + assert(thread_ids[i] > 0 && "Thread creation failed"); + } + + printf("Wait for threads to finish\n"); + for (int i = 0; i < NUM_THREADS; i++) { + if (__builtin_wasm_memory_atomic_wait32(&data[i].th_done, 0, TIMEOUT) + == 2) { + assert(false && "Wait should not time out"); + } + + start_args_deinit(&data[i].base); + } + + printf("Value of count after update: %d\n", g_count); + assert(g_count == (NUM_THREADS * NUM_ITER) + && "Global count not updated correctly"); + + return EXIT_SUCCESS; +} diff --git a/core/iwasm/libraries/lib-wasi-threads/test/global_lock.c b/core/iwasm/libraries/lib-wasi-threads/test/global_lock.c new file mode 100644 index 000000000..282bec71c --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/global_lock.c @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef __wasi__ +#error This example only compiles to WASM/WASI target +#endif + +#include +#include +#include +#include +#include + +#include "wasi_thread_start.h" + +enum CONSTANTS { + NUM_THREADS = 4, + NUM_ITER = 200, + SECOND = 1000 * 1000 * 1000, /* 1 second */ + TIMEOUT = 10LL * SECOND +}; + +pthread_mutex_t mutex; +int g_count = 0; + +typedef struct { + start_args_t base; + int th_done; +} shared_t; + +void +__wasi_thread_start_C(int thread_id, int *start_arg) +{ + shared_t *data = (shared_t *)start_arg; + + for (int i = 0; i < NUM_ITER; i++) { + pthread_mutex_lock(&mutex); + g_count++; + pthread_mutex_unlock(&mutex); + } + + __atomic_store_n(&data->th_done, 1, __ATOMIC_SEQ_CST); + __builtin_wasm_memory_atomic_notify(&data->th_done, 1); +} + +int +main(int argc, char **argv) +{ + shared_t data[NUM_THREADS] = { 0 }; + int thread_ids[NUM_THREADS]; + + assert(pthread_mutex_init(&mutex, NULL) == 0 && "Failed to init mutex"); + + for (int i = 0; i < NUM_THREADS; i++) { + assert(start_args_init(&data[i].base)); + thread_ids[i] = __wasi_thread_spawn(&data[i]); + assert(thread_ids[i] > 0 && "Thread creation failed"); + } + + printf("Wait for threads to finish\n"); + for (int i = 0; i < NUM_THREADS; i++) { + if (__builtin_wasm_memory_atomic_wait32(&data[i].th_done, 0, TIMEOUT) + == 2) { + assert(false && "Wait should not time out"); + } + + start_args_deinit(&data[i].base); + } + + printf("Value of count after update: %d\n", g_count); + assert(g_count == (NUM_THREADS * NUM_ITER) + && "Global count not updated correctly"); + + assert(pthread_mutex_destroy(&mutex) == 0 && "Failed to destroy mutex"); + return EXIT_SUCCESS; +} diff --git a/core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_busy.c b/core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_busy.c new file mode 100644 index 000000000..19d3ec256 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_busy.c @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef __wasi__ +#error This example only compiles to WASM/WASI target +#endif + +#include "common.h" + +int +main(int argc, char **argv) +{ + test_termination(false, true, BLOCKING_TASK_BUSY_WAIT); +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_busy.json b/core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_busy.json new file mode 100644 index 000000000..5370f6670 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_busy.json @@ -0,0 +1,3 @@ +{ + "exit_code": 33 +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_sleep.c b/core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_sleep.c new file mode 100644 index 000000000..a667e9122 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_sleep.c @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef __wasi__ +#error This example only compiles to WASM/WASI target +#endif + +#include "common.h" + +int +main(int argc, char **argv) +{ + test_termination(false, true, BLOCKING_TASK_POLL_ONEOFF); +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_sleep.json b/core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_sleep.json new file mode 100644 index 000000000..5370f6670 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_sleep.json @@ -0,0 +1,3 @@ +{ + "exit_code": 33 +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_wait.c b/core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_wait.c new file mode 100644 index 000000000..dc8615adb --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_wait.c @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef __wasi__ +#error This example only compiles to WASM/WASI target +#endif + +#include "common.h" + +int +main(int argc, char **argv) +{ + test_termination(false, true, BLOCKING_TASK_ATOMIC_WAIT); +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_wait.json b/core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_wait.json new file mode 100644 index 000000000..5370f6670 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/main_proc_exit_wait.json @@ -0,0 +1,3 @@ +{ + "exit_code": 33 +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/main_trap_busy.c b/core/iwasm/libraries/lib-wasi-threads/test/main_trap_busy.c new file mode 100644 index 000000000..bb0ac8fa0 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/main_trap_busy.c @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef __wasi__ +#error This example only compiles to WASM/WASI target +#endif + +#include "common.h" + +int +main(int argc, char **argv) +{ + test_termination(true, true, BLOCKING_TASK_BUSY_WAIT); +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/main_trap_busy.json b/core/iwasm/libraries/lib-wasi-threads/test/main_trap_busy.json new file mode 100644 index 000000000..07689a105 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/main_trap_busy.json @@ -0,0 +1,3 @@ +{ + "exit_code": 1 +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/main_trap_sleep.c b/core/iwasm/libraries/lib-wasi-threads/test/main_trap_sleep.c new file mode 100644 index 000000000..a2c248882 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/main_trap_sleep.c @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef __wasi__ +#error This example only compiles to WASM/WASI target +#endif + +#include "common.h" + +int +main(int argc, char **argv) +{ + test_termination(true, true, BLOCKING_TASK_POLL_ONEOFF); +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/main_trap_sleep.json b/core/iwasm/libraries/lib-wasi-threads/test/main_trap_sleep.json new file mode 100644 index 000000000..07689a105 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/main_trap_sleep.json @@ -0,0 +1,3 @@ +{ + "exit_code": 1 +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/main_trap_wait.c b/core/iwasm/libraries/lib-wasi-threads/test/main_trap_wait.c new file mode 100644 index 000000000..0904f34bb --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/main_trap_wait.c @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef __wasi__ +#error This example only compiles to WASM/WASI target +#endif + +#include "common.h" + +int +main(int argc, char **argv) +{ + test_termination(true, true, BLOCKING_TASK_ATOMIC_WAIT); +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/main_trap_wait.json b/core/iwasm/libraries/lib-wasi-threads/test/main_trap_wait.json new file mode 100644 index 000000000..07689a105 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/main_trap_wait.json @@ -0,0 +1,3 @@ +{ + "exit_code": 1 +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_busy.c b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_busy.c new file mode 100644 index 000000000..71fdcb817 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_busy.c @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef __wasi__ +#error This example only compiles to WASM/WASI target +#endif + +#include "common.h" + +int +main(int argc, char **argv) +{ + test_termination(false, false, BLOCKING_TASK_BUSY_WAIT); +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_busy.json b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_busy.json new file mode 100644 index 000000000..5370f6670 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_busy.json @@ -0,0 +1,3 @@ +{ + "exit_code": 33 +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_sleep.c b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_sleep.c new file mode 100644 index 000000000..14352cf41 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_sleep.c @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef __wasi__ +#error This example only compiles to WASM/WASI target +#endif + +#include "common.h" + +int +main(int argc, char **argv) +{ + test_termination(false, false, BLOCKING_TASK_POLL_ONEOFF); +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_sleep.json b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_sleep.json new file mode 100644 index 000000000..5370f6670 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_sleep.json @@ -0,0 +1,3 @@ +{ + "exit_code": 33 +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_wait.c b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_wait.c new file mode 100644 index 000000000..0963aa02f --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_wait.c @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef __wasi__ +#error This example only compiles to WASM/WASI target +#endif + +#include "common.h" + +int +main(int argc, char **argv) +{ + test_termination(false, false, BLOCKING_TASK_ATOMIC_WAIT); +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_wait.json b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_wait.json new file mode 100644 index 000000000..5370f6670 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_proc_exit_wait.json @@ -0,0 +1,3 @@ +{ + "exit_code": 33 +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_busy.c b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_busy.c new file mode 100644 index 000000000..b3e3af7dc --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_busy.c @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef __wasi__ +#error This example only compiles to WASM/WASI target +#endif + +#include "common.h" + +int +main(int argc, char **argv) +{ + test_termination(true, false, BLOCKING_TASK_BUSY_WAIT); +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_busy.json b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_busy.json new file mode 100644 index 000000000..07689a105 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_busy.json @@ -0,0 +1,3 @@ +{ + "exit_code": 1 +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_sleep.c b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_sleep.c new file mode 100644 index 000000000..a68ae8be5 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_sleep.c @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef __wasi__ +#error This example only compiles to WASM/WASI target +#endif + +#include "common.h" + +int +main(int argc, char **argv) +{ + test_termination(true, false, BLOCKING_TASK_POLL_ONEOFF); +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_sleep.json b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_sleep.json new file mode 100644 index 000000000..07689a105 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_sleep.json @@ -0,0 +1,3 @@ +{ + "exit_code": 1 +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_wait.c b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_wait.c new file mode 100644 index 000000000..52c684a51 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_wait.c @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef __wasi__ +#error This example only compiles to WASM/WASI target +#endif + +#include "common.h" + +int +main(int argc, char **argv) +{ + test_termination(true, false, BLOCKING_TASK_ATOMIC_WAIT); +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_wait.json b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_wait.json new file mode 100644 index 000000000..07689a105 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/nonmain_trap_wait.json @@ -0,0 +1,3 @@ +{ + "exit_code": 1 +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/spawn_multiple_times.c b/core/iwasm/libraries/lib-wasi-threads/test/spawn_multiple_times.c new file mode 100644 index 000000000..24664c470 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/spawn_multiple_times.c @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef __wasi__ +#error This example only compiles to WASM/WASI target +#endif + +#include +#include +#include +#include + +#include "wasi_thread_start.h" + +enum CONSTANTS { + NUM_ITER = 50, + NUM_RETRY = 5, + SECOND = 1000 * 1000 * 1000, /* 1 second */ + TIMEOUT = 5LL * SECOND +}; + +typedef struct { + start_args_t base; + int th_done; +} shared_t; + +int g_count = 0; + +void +__wasi_thread_start_C(int thread_id, int *start_arg) +{ + shared_t *data = (shared_t *)start_arg; + + g_count++; + + __atomic_store_n(&data->th_done, 1, __ATOMIC_SEQ_CST); + __builtin_wasm_memory_atomic_notify(&data->th_done, 1); +} + +int +main(int argc, char **argv) +{ + shared_t data = { 0 }; + assert(start_args_init(&data.base) && "Stack allocation for thread failed"); + + for (int i = 0; i < NUM_ITER; i++) { + data.th_done = 0; + + printf("Creating thread\n"); + int thread_id = -1; + for (int j = 0; j < NUM_RETRY && thread_id < 0; j++) { + thread_id = __wasi_thread_spawn(&data); + if (thread_id < 0) + __builtin_wasm_memory_atomic_wait32(NULL, 0, SECOND); + } + assert(thread_id > 0 && "Thread creation should succeed"); + + printf("Waiting for thread to finish\n"); + if (__builtin_wasm_memory_atomic_wait32(&data.th_done, 0, TIMEOUT) + == 2) { + assert(false && "Wait should not time out"); + } + printf("Thread has finished\n"); + } + + assert(g_count == NUM_ITER && "Count has not been updated correctly"); + + start_args_deinit(&data.base); + return EXIT_SUCCESS; +} diff --git a/core/iwasm/libraries/lib-wasi-threads/test/update_shared_data_and_alloc_heap.c b/core/iwasm/libraries/lib-wasi-threads/test/update_shared_data_and_alloc_heap.c new file mode 100644 index 000000000..b8cf56495 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/update_shared_data_and_alloc_heap.c @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef __wasi__ +#error This example only compiles to WASM/WASI target +#endif + +#include +#include +#include +#include + +#include "wasi_thread_start.h" + +enum CONSTANTS { + NUM_THREADS = 4, + NUM_ITER = 30, + SECOND = 1000 * 1000 * 1000, /* 1 second */ + TIMEOUT = 10LL * SECOND +}; + +typedef struct { + start_args_t base; + int th_done; + int *count; + int iteration; + int *pval; +} shared_t; + +int *vals[NUM_THREADS]; + +void +__wasi_thread_start_C(int thread_id, int *start_arg) +{ + shared_t *data = (shared_t *)start_arg; + + for (int i = 0; i < NUM_ITER; i++) + __atomic_fetch_add(data->count, 1, __ATOMIC_SEQ_CST); + + vals[data->iteration] = malloc(sizeof(int)); + *vals[data->iteration] = data->iteration; + + __atomic_store_n(&data->th_done, 1, __ATOMIC_SEQ_CST); + __builtin_wasm_memory_atomic_notify(&data->th_done, 1); +} + +int +main(int argc, char **argv) +{ + shared_t data[NUM_THREADS] = { 0 }; + int thread_ids[NUM_THREADS]; + int *count = calloc(1, sizeof(int)); + + for (int i = 0; i < NUM_THREADS; i++) { + assert(start_args_init(&data[i].base) + && "Stack allocation for thread failed"); + __atomic_store_n(&data[i].count, count, __ATOMIC_SEQ_CST); + data[i].iteration = i; + + thread_ids[i] = __wasi_thread_spawn(&data[i]); + assert(thread_ids[i] > 0 && "Thread creation failed"); + } + + printf("Wait for threads to finish\n"); + for (int i = 0; i < NUM_THREADS; i++) { + if (__builtin_wasm_memory_atomic_wait32(&data[i].th_done, 0, TIMEOUT) + == 2) { + assert(false && "Wait should not time out"); + } + + start_args_deinit(&data[i].base); + } + + assert(*count == (NUM_THREADS * NUM_ITER) && "Count not updated correctly"); + + for (int i = 0; i < NUM_THREADS; i++) { + printf("val=%d\n", *vals[i]); + assert(*vals[i] == i && "Value not updated correctly"); + free(vals[i]); + } + + free(count); + return EXIT_SUCCESS; +} diff --git a/core/iwasm/libraries/lib-wasi-threads/tid_allocator.c b/core/iwasm/libraries/lib-wasi-threads/tid_allocator.c new file mode 100644 index 000000000..4d53da0c9 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/tid_allocator.c @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "tid_allocator.h" +#include "wasm_export.h" +#include "bh_log.h" + +bh_static_assert(TID_MIN <= TID_MAX); +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + +bool +tid_allocator_init(TidAllocator *tid_allocator) +{ + tid_allocator->size = MIN(TID_ALLOCATOR_INIT_SIZE, TID_MAX - TID_MIN + 1); + tid_allocator->pos = tid_allocator->size; + tid_allocator->ids = + wasm_runtime_malloc(tid_allocator->size * sizeof(int32)); + if (tid_allocator->ids == NULL) + return false; + + for (int64 i = tid_allocator->pos - 1; i >= 0; i--) + tid_allocator->ids[i] = TID_MIN + (tid_allocator->pos - 1 - i); + + return true; +} + +void +tid_allocator_deinit(TidAllocator *tid_allocator) +{ + wasm_runtime_free(tid_allocator->ids); +} + +int32 +tid_allocator_get_tid(TidAllocator *tid_allocator) +{ + if (tid_allocator->pos == 0) { // Resize stack and push new thread ids + if (tid_allocator->size == TID_MAX - TID_MIN + 1) { + LOG_ERROR("Maximum thread identifier reached"); + return -1; + } + + uint32 old_size = tid_allocator->size; + uint32 new_size = MIN(tid_allocator->size * 2, TID_MAX - TID_MIN + 1); + if (new_size != TID_MAX - TID_MIN + 1 + && new_size / 2 != tid_allocator->size) { + LOG_ERROR("Overflow detected during new size calculation"); + return -1; + } + + size_t realloc_size = new_size * sizeof(int32); + if (realloc_size / sizeof(int32) != new_size) { + LOG_ERROR("Overflow detected during realloc"); + return -1; + } + int32 *tmp = wasm_runtime_realloc(tid_allocator->ids, realloc_size); + if (tmp == NULL) { + LOG_ERROR("Thread ID allocator realloc failed"); + return -1; + } + + tid_allocator->size = new_size; + tid_allocator->pos = new_size - old_size; + tid_allocator->ids = tmp; + for (int64 i = tid_allocator->pos - 1; i >= 0; i--) + tid_allocator->ids[i] = TID_MIN + (tid_allocator->size - 1 - i); + } + + // Pop available thread identifier from the stack + return tid_allocator->ids[--tid_allocator->pos]; +} + +void +tid_allocator_release_tid(TidAllocator *tid_allocator, int32 thread_id) +{ + // Release thread identifier by pushing it into the stack + bh_assert(tid_allocator->pos < tid_allocator->size); + tid_allocator->ids[tid_allocator->pos++] = thread_id; +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/tid_allocator.h b/core/iwasm/libraries/lib-wasi-threads/tid_allocator.h new file mode 100644 index 000000000..53af1719f --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/tid_allocator.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _TID_ALLOCATOR_H +#define _TID_ALLOCATOR_H + +#include "platform_common.h" + +#define TID_ALLOCATOR_INIT_SIZE CLUSTER_MAX_THREAD_NUM +enum { + TID_MIN = 1, + TID_MAX = 0x1FFFFFFF +}; // Reserved TIDs (WASI specification) + +/* Stack data structure to track available thread identifiers */ +typedef struct { + int32 *ids; // Array used to store the stack + uint32 size; // Stack capacity + uint32 pos; // Index of the element after the stack top +} TidAllocator; + +bool +tid_allocator_init(TidAllocator *tid_allocator); + +void +tid_allocator_deinit(TidAllocator *tid_allocator); + +int32 +tid_allocator_get_tid(TidAllocator *tid_allocator); + +void +tid_allocator_release_tid(TidAllocator *tid_allocator, int32 thread_id); + +#endif /* _TID_ALLOCATOR_H */ \ No newline at end of file diff --git a/core/iwasm/libraries/libc-emcc/libc_emcc_wrapper.c b/core/iwasm/libraries/libc-emcc/libc_emcc_wrapper.c index e35f3908d..23c02aad3 100644 --- a/core/iwasm/libraries/libc-emcc/libc_emcc_wrapper.c +++ b/core/iwasm/libraries/libc-emcc/libc_emcc_wrapper.c @@ -37,10 +37,6 @@ wasm_runtime_module_free(module_inst, offset) /* clang-format on */ -extern bool -wasm_runtime_call_indirect(wasm_exec_env_t exec_env, uint32 element_idx, - uint32 argc, uint32 argv[]); - static void invoke_viiii_wrapper(wasm_exec_env_t exec_env, uint32 elem_idx, int arg0, int arg1, int arg2, int arg3) diff --git a/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.c b/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.c index a6a448b50..ab2808c6e 100644 --- a/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.c +++ b/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.c @@ -6,6 +6,11 @@ #include "libc_wasi_wrapper.h" #include "bh_platform.h" #include "wasm_export.h" +#include "wasm_runtime_common.h" + +#if WASM_ENABLE_THREAD_MGR != 0 +#include "../../../thread-mgr/thread_manager.h" +#endif void wasm_runtime_set_exception(wasm_module_inst_t module, const char *exception); @@ -46,23 +51,17 @@ typedef struct iovec_app { uint32 buf_len; } iovec_app_t; -typedef struct WASIContext { - struct fd_table *curfds; - struct fd_prestats *prestats; - struct argv_environ_values *argv_environ; - struct addr_pool *addr_pool; - char *ns_lookup_buf; - char **ns_lookup_list; - char *argv_buf; - char **argv_list; - char *env_buf; - char **env_list; - uint32_t exit_code; -} * wasi_ctx_t; +typedef struct WASIContext *wasi_ctx_t; wasi_ctx_t wasm_runtime_get_wasi_ctx(wasm_module_inst_t module_inst); +static inline size_t +min(size_t a, size_t b) +{ + return a > b ? b : a; +} + static inline struct fd_table * wasi_ctx_get_curfds(wasm_module_inst_t module_inst, wasi_ctx_t wasi_ctx) { @@ -951,6 +950,108 @@ wasi_path_remove_directory(wasm_exec_env_t exec_env, wasi_fd_t fd, return wasmtime_ssp_path_remove_directory(curfds, fd, path, path_len); } +#if WASM_ENABLE_THREAD_MGR != 0 +static __wasi_timestamp_t +get_timeout_for_poll_oneoff(const wasi_subscription_t *in, + uint32 nsubscriptions) +{ + __wasi_timestamp_t timeout = (__wasi_timestamp_t)-1; + uint32 i = 0; + + for (i = 0; i < nsubscriptions; ++i) { + const __wasi_subscription_t *s = &in[i]; + if (s->u.type == __WASI_EVENTTYPE_CLOCK + && (s->u.u.clock.flags & __WASI_SUBSCRIPTION_CLOCK_ABSTIME) == 0) { + timeout = min(timeout, s->u.u.clock.timeout); + } + } + return timeout; +} + +static void +update_clock_subscription_data(wasi_subscription_t *in, uint32 nsubscriptions, + const wasi_timestamp_t new_timeout) +{ + uint32 i = 0; + for (i = 0; i < nsubscriptions; ++i) { + __wasi_subscription_t *s = &in[i]; + if (s->u.type == __WASI_EVENTTYPE_CLOCK) { + s->u.u.clock.timeout = new_timeout; + } + } +} + +static wasi_errno_t +execute_interruptible_poll_oneoff( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + const __wasi_subscription_t *in, __wasi_event_t *out, size_t nsubscriptions, + size_t *nevents, wasm_exec_env_t exec_env) +{ + if (nsubscriptions == 0) { + *nevents = 0; + return __WASI_ESUCCESS; + } + + wasi_errno_t err; + __wasi_timestamp_t elapsed = 0; + bool all_outs_are_type_clock; + uint32 i; + + const __wasi_timestamp_t timeout = get_timeout_for_poll_oneoff( + in, nsubscriptions), + time_quant = 1e9; + const uint64 size_to_copy = + nsubscriptions * (uint64)sizeof(wasi_subscription_t); + __wasi_subscription_t *in_copy = NULL; + + if (size_to_copy >= UINT32_MAX + || !(in_copy = (__wasi_subscription_t *)wasm_runtime_malloc( + (uint32)size_to_copy))) { + return __WASI_ENOMEM; + } + + bh_memcpy_s(in_copy, size_to_copy, in, size_to_copy); + + while (timeout == (__wasi_timestamp_t)-1 || elapsed <= timeout) { + /* update timeout for clock subscription events */ + update_clock_subscription_data(in_copy, nsubscriptions, + min(time_quant, timeout - elapsed)); + err = wasmtime_ssp_poll_oneoff(curfds, in_copy, out, nsubscriptions, + nevents); + elapsed += time_quant; + + if (err) { + wasm_runtime_free(in_copy); + return err; + } + + if (wasm_cluster_is_thread_terminated(exec_env)) { + wasm_runtime_free(in_copy); + return EINTR; + } + else if (*nevents > 0) { + all_outs_are_type_clock = true; + for (i = 0; i < *nevents; i++) { + if (out[i].type != __WASI_EVENTTYPE_CLOCK) { + all_outs_are_type_clock = false; + break; + } + } + + if (!all_outs_are_type_clock) { + wasm_runtime_free(in_copy); + return __WASI_ESUCCESS; + } + } + } + + wasm_runtime_free(in_copy); + return __WASI_ESUCCESS; +} +#endif + static wasi_errno_t wasi_poll_oneoff(wasm_exec_env_t exec_env, const wasi_subscription_t *in, wasi_event_t *out, uint32 nsubscriptions, uint32 *nevents_app) @@ -958,7 +1059,7 @@ wasi_poll_oneoff(wasm_exec_env_t exec_env, const wasi_subscription_t *in, wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx); - size_t nevents; + size_t nevents = 0; wasi_errno_t err; if (!wasi_ctx) @@ -969,7 +1070,12 @@ wasi_poll_oneoff(wasm_exec_env_t exec_env, const wasi_subscription_t *in, || !validate_native_addr(nevents_app, sizeof(uint32))) return (wasi_errno_t)-1; +#if WASM_ENABLE_THREAD_MGR == 0 err = wasmtime_ssp_poll_oneoff(curfds, in, out, nsubscriptions, &nevents); +#else + err = execute_interruptible_poll_oneoff(curfds, in, out, nsubscriptions, + &nevents, exec_env); +#endif if (err) return err; @@ -1861,12 +1967,6 @@ allocate_iovec_app_buffer(wasm_module_inst_t module_inst, return __WASI_ESUCCESS; } -static inline size_t -min(size_t a, size_t b) -{ - return a > b ? b : a; -} - static wasi_errno_t copy_buffer_to_iovec_app(wasm_module_inst_t module_inst, uint8 *buf_begin, uint32 buf_size, iovec_app_t *data, uint32 data_len, diff --git a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/gnuc.h b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/gnuc.h new file mode 100644 index 000000000..70000ae0b --- /dev/null +++ b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/gnuc.h @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2023 Amazon.com, Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#if !defined(__GNUC_PREREQ) && (defined(__GNUC__) || defined(__GNUG__)) \ + && !defined(__clang__) && defined(__GNUC_MINOR__) +/* Depending on the platform the macro is defined in sys/features.h or + features.h Given the macro is simple, we re-implement it here instead of + dealing with two different paths. + */ +#define __GNUC_PREREQ(maj, min) \ + ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min)) +#endif diff --git a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/numeric_limits.h b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/numeric_limits.h deleted file mode 100644 index 233f3733b..000000000 --- a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/numeric_limits.h +++ /dev/null @@ -1,42 +0,0 @@ -// Part of the Wasmtime Project, under the Apache License v2.0 with LLVM -// Exceptions. See -// https://github.com/bytecodealliance/wasmtime/blob/main/LICENSE for license -// information. -// -// Significant parts of this file are derived from cloudabi-utils. See -// https://github.com/bytecodealliance/wasmtime/blob/main/lib/wasi/sandboxed-system-primitives/src/LICENSE -// for license information. -// -// The upstream file contains the following copyright notice: -// -// Copyright (c) 2015 Nuxi, https://nuxi.nl/ - -#ifndef COMMON_LIMITS_H -#define COMMON_LIMITS_H - -#define NUMERIC_MIN(t) \ - _Generic((t)0, char \ - : CHAR_MIN, signed char \ - : SCHAR_MIN, unsigned char : 0, short \ - : SHRT_MIN, unsigned short : 0, int \ - : INT_MIN, unsigned int : 0, long \ - : LONG_MIN, unsigned long : 0, long long \ - : LLONG_MIN, unsigned long long : 0, default \ - : (void)0) - -#define NUMERIC_MAX(t) \ - _Generic((t)0, char \ - : CHAR_MAX, signed char \ - : SCHAR_MAX, unsigned char \ - : UCHAR_MAX, short \ - : SHRT_MAX, unsigned short \ - : USHRT_MAX, int \ - : INT_MAX, unsigned int \ - : UINT_MAX, long \ - : LONG_MAX, unsigned long \ - : ULONG_MAX, long long \ - : LLONG_MAX, unsigned long long \ - : ULLONG_MAX, default \ - : (void)0) - -#endif diff --git a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/posix.c b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/posix.c index d8c57459b..9e29a3bac 100644 --- a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/posix.c +++ b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/posix.c @@ -15,7 +15,6 @@ #include "bh_platform.h" #include "wasmtime_ssp.h" #include "locking.h" -#include "numeric_limits.h" #include "posix.h" #include "random.h" #include "refcount.h" @@ -686,9 +685,20 @@ fd_table_insert_existing(struct fd_table *ft, __wasi_fd_t in, int out) struct fd_object *fo; __wasi_errno_t error; - if (fd_determine_type_rights(out, &type, &rights_base, &rights_inheriting) - != 0) + error = + fd_determine_type_rights(out, &type, &rights_base, &rights_inheriting); + if (error != 0) { +#ifdef BH_PLATFORM_EGO + /** + * since it is an already opened file and we can assume the opened file + * has all necessary rights no matter how to get + */ + if (error != __WASI_ENOTSUP) + return false; +#else return false; +#endif + } error = fd_object_new(type, &fo); if (error != 0) @@ -2257,8 +2267,7 @@ convert_timestamp(__wasi_timestamp_t in, struct timespec *out) in /= 1000000000; // Clamp to the maximum in case it would overflow our system's time_t. - out->tv_sec = - (time_t)in < NUMERIC_MAX(time_t) ? (time_t)in : NUMERIC_MAX(time_t); + out->tv_sec = (time_t)in < BH_TIME_T_MAX ? (time_t)in : BH_TIME_T_MAX; } // Converts the provided timestamps and flags to a set of arguments for @@ -2604,6 +2613,8 @@ wasmtime_ssp_poll_oneoff( } #endif *nevents = 1; + if (out[0].error != 0) + return convert_errno(out[0].error); return 0; } @@ -3226,6 +3237,7 @@ wasi_ssp_sock_get_reuse_port( #else errno = ENOTSUP; ret = BHT_ERROR; + optval = 0; #endif /* defined(SO_REUSEPORT) */ fd_object_release(fo); diff --git a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/refcount.h b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/refcount.h index ac348ebb4..03b4b87ac 100644 --- a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/refcount.h +++ b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/refcount.h @@ -16,6 +16,7 @@ #include "bh_platform.h" #include "locking.h" +#include "gnuc.h" #define PRODUCES(...) LOCKS_SHARED(__VA_ARGS__) NO_LOCK_ANALYSIS #define CONSUMES(...) UNLOCKS(__VA_ARGS__) NO_LOCK_ANALYSIS @@ -95,6 +96,42 @@ refcount_release(struct refcount *r) return old == 1; } +#elif defined(__GNUC_PREREQ) + +#if __GNUC_PREREQ(4, 7) + +struct refcount { + unsigned int count; +}; + +/* Initialize the reference counter. */ +static inline void +refcount_init(struct refcount *r, unsigned int count) +{ + __atomic_store_n(&r->count, count, __ATOMIC_SEQ_CST); +} + +/* Increment the reference counter. */ +static inline void +refcount_acquire(struct refcount *r) +{ + __atomic_fetch_add(&r->count, 1, __ATOMIC_ACQUIRE); +} + +/* Decrement the reference counter, returning whether the reference + dropped to zero. */ +static inline bool +refcount_release(struct refcount *r) +{ + int old = (int)__atomic_fetch_sub(&r->count, 1, __ATOMIC_RELEASE); + bh_assert(old != 0 && "Reference count becoming negative"); + return old == 1; +} + +#else /* else of __GNUC_PREREQ (4.7) */ +#error "Reference counter isn't implemented" +#endif /* end of __GNUC_PREREQ (4.7) */ + #else /* else of CONFIG_HAS_STD_ATOMIC */ #error "Reference counter isn't implemented" #endif /* end of CONFIG_HAS_STD_ATOMIC */ diff --git a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/ssp_config.h b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/ssp_config.h index 68b0faf34..d5babd02b 100644 --- a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/ssp_config.h +++ b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/ssp_config.h @@ -14,6 +14,7 @@ #ifndef SSP_CONFIG_H #define SSP_CONFIG_H +#include "gnuc.h" #include #if defined(__FreeBSD__) || defined(__APPLE__) \ @@ -107,10 +108,31 @@ #endif #if !defined(BH_PLATFORM_LINUX_SGX) +/* Clang's __GNUC_PREREQ macro has a different meaning than GCC one, +so we have to handle this case specially */ +#if defined(__clang__) +/* Clang provides stdatomic.h since 3.6.0 +See https://releases.llvm.org/3.6.0/tools/clang/docs/ReleaseNotes.html */ +#if __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 6) #define CONFIG_HAS_STD_ATOMIC 1 #else #define CONFIG_HAS_STD_ATOMIC 0 #endif +#elif defined(__GNUC_PREREQ) +/* Even though older versions of GCC support C11, atomics were +not implemented until 4.9. See +https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58016 */ +#if __GNUC_PREREQ(4, 9) +#define CONFIG_HAS_STD_ATOMIC 1 +#else /* else of __GNUC_PREREQ(4, 9) */ +#define CONFIG_HAS_STD_ATOMIC 0 +#endif /* end of __GNUC_PREREQ(4, 9) */ +#else /* else of defined(__GNUC_PREREQ) */ +#define CONFIG_HAS_STD_ATOMIC 1 +#endif /* end of defined(__GNUC_PREREQ) */ +#else /* else of !defined(BH_PLATFORM_LINUX_SGX) */ +#define CONFIG_HAS_STD_ATOMIC 0 +#endif /* end of !defined(BH_PLATFORM_LINUX_SGX) */ #if !defined(__NuttX__) #define CONFIG_HAS_D_INO 1 diff --git a/core/iwasm/libraries/thread-mgr/thread_manager.c b/core/iwasm/libraries/thread-mgr/thread_manager.c index 4408b0133..e61915d52 100644 --- a/core/iwasm/libraries/thread-mgr/thread_manager.c +++ b/core/iwasm/libraries/thread-mgr/thread_manager.c @@ -16,6 +16,10 @@ #include "debug_engine.h" #endif +#if WASM_ENABLE_SHARED_MEMORY != 0 +#include "wasm_shared_memory.h" +#endif + typedef struct { bh_list_link l; void (*destroy_cb)(WASMCluster *); @@ -76,9 +80,76 @@ traverse_list(bh_list *l, list_visitor visitor, void *user_data) } } +/* Assumes cluster->lock is locked */ static bool -allocate_aux_stack(WASMCluster *cluster, uint32 *start, uint32 *size) +safe_traverse_exec_env_list(WASMCluster *cluster, list_visitor visitor, + void *user_data) { + Vector proc_nodes; + void *node; + bool ret = true; + + if (!bh_vector_init(&proc_nodes, cluster->exec_env_list.len, sizeof(void *), + false)) { + ret = false; + goto final; + } + + node = bh_list_first_elem(&cluster->exec_env_list); + + while (node) { + bool already_processed = false; + void *proc_node; + uint32 i; + for (i = 0; i < (uint32)bh_vector_size(&proc_nodes); i++) { + if (!bh_vector_get(&proc_nodes, i, &proc_node)) { + ret = false; + goto final; + } + if (proc_node == node) { + already_processed = true; + break; + } + } + if (already_processed) { + node = bh_list_elem_next(node); + continue; + } + + os_mutex_unlock(&cluster->lock); + visitor(node, user_data); + os_mutex_lock(&cluster->lock); + if (!bh_vector_append(&proc_nodes, &node)) { + ret = false; + goto final; + } + + node = bh_list_first_elem(&cluster->exec_env_list); + } + +final: + bh_vector_destroy(&proc_nodes); + + return ret; +} + +/* The caller must lock cluster->lock */ +static bool +allocate_aux_stack(WASMExecEnv *exec_env, uint32 *start, uint32 *size) +{ + WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env); +#if WASM_ENABLE_HEAP_AUX_STACK_ALLOCATION != 0 + WASMModuleInstanceCommon *module_inst = + wasm_exec_env_get_module_inst(exec_env); + uint32 stack_end; + + stack_end = + wasm_runtime_module_malloc(module_inst, cluster->stack_size, NULL); + *start = stack_end + cluster->stack_size; + *size = cluster->stack_size; + + return stack_end != 0; +#else uint32 i; /* If the module doesn't have aux stack info, @@ -86,7 +157,6 @@ allocate_aux_stack(WASMCluster *cluster, uint32 *start, uint32 *size) if (!cluster->stack_segment_occupied) return false; - os_mutex_lock(&cluster->lock); for (i = 0; i < cluster_max_thread_num; i++) { if (!cluster->stack_segment_occupied[i]) { if (start) @@ -94,36 +164,51 @@ allocate_aux_stack(WASMCluster *cluster, uint32 *start, uint32 *size) if (size) *size = cluster->stack_size; cluster->stack_segment_occupied[i] = true; - os_mutex_unlock(&cluster->lock); return true; } } - os_mutex_unlock(&cluster->lock); + return false; +#endif } +/* The caller must lock cluster->lock */ static bool -free_aux_stack(WASMCluster *cluster, uint32 start) +free_aux_stack(WASMExecEnv *exec_env, uint32 start) { + WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env); + +#if WASM_ENABLE_HEAP_AUX_STACK_ALLOCATION != 0 + WASMModuleInstanceCommon *module_inst = + wasm_exec_env_get_module_inst(exec_env); + + if (!wasm_exec_env_is_aux_stack_managed_by_runtime(exec_env)) { + return true; + } + + bh_assert(start >= cluster->stack_size); + + wasm_runtime_module_free(module_inst, start - cluster->stack_size); + + return true; +#else uint32 i; for (i = 0; i < cluster_max_thread_num; i++) { if (start == cluster->stack_tops[i]) { - os_mutex_lock(&cluster->lock); cluster->stack_segment_occupied[i] = false; - os_mutex_unlock(&cluster->lock); return true; } } return false; +#endif } WASMCluster * wasm_cluster_create(WASMExecEnv *exec_env) { WASMCluster *cluster; - uint64 total_size; - uint32 aux_stack_start, aux_stack_size, i; + uint32 aux_stack_start, aux_stack_size; bh_assert(exec_env->cluster == NULL); if (!(cluster = wasm_runtime_malloc(sizeof(WASMCluster)))) { @@ -145,7 +230,9 @@ wasm_cluster_create(WASMExecEnv *exec_env) /* Prepare the aux stack top and size for every thread */ if (!wasm_exec_env_get_aux_stack(exec_env, &aux_stack_start, &aux_stack_size)) { +#if WASM_ENABLE_LIB_WASI_THREADS == 0 LOG_VERBOSE("No aux stack info for this module, can't create thread"); +#endif /* If the module don't have aux stack info, don't throw error here, but remain stack_tops and stack_segment_occupied as NULL */ @@ -159,12 +246,16 @@ wasm_cluster_create(WASMExecEnv *exec_env) return cluster; } +#if WASM_ENABLE_HEAP_AUX_STACK_ALLOCATION != 0 + cluster->stack_size = aux_stack_size; +#else cluster->stack_size = aux_stack_size / (cluster_max_thread_num + 1); if (cluster->stack_size < WASM_THREAD_AUX_STACK_SIZE_MIN) { goto fail; } /* Make stack size 16-byte aligned */ cluster->stack_size = cluster->stack_size & (~15); +#endif /* Set initial aux stack top to the instance and aux stack boundary to the main exec_env */ @@ -172,8 +263,10 @@ wasm_cluster_create(WASMExecEnv *exec_env) cluster->stack_size)) goto fail; +#if WASM_ENABLE_HEAP_AUX_STACK_ALLOCATION == 0 if (cluster_max_thread_num != 0) { - total_size = cluster_max_thread_num * sizeof(uint32); + uint64 total_size = cluster_max_thread_num * sizeof(uint32); + uint32 i; if (total_size >= UINT32_MAX || !(cluster->stack_tops = wasm_runtime_malloc((uint32)total_size))) { @@ -195,6 +288,7 @@ wasm_cluster_create(WASMExecEnv *exec_env) cluster->stack_tops[i] = aux_stack_start - cluster->stack_size * i; } } +#endif os_mutex_lock(&cluster_list_lock); if (bh_list_insert(cluster_list, cluster) != 0) { @@ -234,10 +328,12 @@ wasm_cluster_destroy(WASMCluster *cluster) os_mutex_destroy(&cluster->lock); +#if WASM_ENABLE_HEAP_AUX_STACK_ALLOCATION == 0 if (cluster->stack_tops) wasm_runtime_free(cluster->stack_tops); if (cluster->stack_segment_occupied) wasm_runtime_free(cluster->stack_segment_occupied); +#endif #if WASM_ENABLE_DEBUG_INTERP != 0 wasm_debug_instance_destroy(cluster); @@ -265,20 +361,27 @@ wasm_exec_env_get_cluster(WASMExecEnv *exec_env) return exec_env->cluster; } -bool +/* The caller must lock cluster->lock */ +static bool wasm_cluster_add_exec_env(WASMCluster *cluster, WASMExecEnv *exec_env) { bool ret = true; exec_env->cluster = cluster; - os_mutex_lock(&cluster->lock); - if (bh_list_insert(&cluster->exec_env_list, exec_env) != 0) + if (cluster->exec_env_list.len == cluster_max_thread_num + 1) { + LOG_ERROR("thread manager error: " + "maximum number of threads exceeded"); ret = false; - os_mutex_unlock(&cluster->lock); + } + + if (ret && bh_list_insert(&cluster->exec_env_list, exec_env) != 0) + ret = false; + return ret; } +/* The caller should lock cluster->lock for thread safety */ bool wasm_cluster_del_exec_env(WASMCluster *cluster, WASMExecEnv *exec_env) { @@ -300,11 +403,8 @@ wasm_cluster_del_exec_env(WASMCluster *cluster, WASMExecEnv *exec_env) os_mutex_unlock(&cluster->debug_inst->wait_lock); } #endif - - os_mutex_lock(&cluster->lock); if (bh_list_remove(&cluster->exec_env_list, exec_env) != 0) ret = false; - os_mutex_unlock(&cluster->lock); if (cluster->exec_env_list.len == 0) { /* exec_env_list empty, destroy the cluster */ @@ -374,6 +474,12 @@ wasm_cluster_spawn_exec_env(WASMExecEnv *exec_env) return NULL; } + os_mutex_lock(&cluster->lock); + + if (cluster->has_exception || cluster->processing) { + goto fail1; + } + #if WASM_ENABLE_INTERP != 0 if (module_inst->module_type == Wasm_Module_Bytecode) { stack_size = @@ -390,7 +496,7 @@ wasm_cluster_spawn_exec_env(WASMExecEnv *exec_env) if (!(new_module_inst = wasm_runtime_instantiate_internal( module, true, stack_size, 0, NULL, 0))) { - return NULL; + goto fail1; } /* Set custom_data to new module instance */ @@ -405,32 +511,36 @@ wasm_cluster_spawn_exec_env(WASMExecEnv *exec_env) new_exec_env = wasm_exec_env_create_internal(new_module_inst, exec_env->wasm_stack_size); if (!new_exec_env) - goto fail1; + goto fail2; - if (!allocate_aux_stack(cluster, &aux_stack_start, &aux_stack_size)) { + if (!allocate_aux_stack(exec_env, &aux_stack_start, &aux_stack_size)) { LOG_ERROR("thread manager error: " "failed to allocate aux stack space for new thread"); - goto fail2; + goto fail3; } /* Set aux stack for current thread */ if (!wasm_exec_env_set_aux_stack(new_exec_env, aux_stack_start, aux_stack_size)) { - goto fail3; + goto fail4; } if (!wasm_cluster_add_exec_env(cluster, new_exec_env)) - goto fail3; + goto fail4; + + os_mutex_unlock(&cluster->lock); return new_exec_env; -fail3: +fail4: /* free the allocated aux stack space */ - free_aux_stack(cluster, aux_stack_start); + free_aux_stack(exec_env, aux_stack_start); +fail3: + wasm_exec_env_destroy_internal(new_exec_env); fail2: - wasm_exec_env_destroy(new_exec_env); -fail1: wasm_runtime_deinstantiate_internal(new_module_inst, true); +fail1: + os_mutex_unlock(&cluster->lock); return NULL; } @@ -442,12 +552,18 @@ wasm_cluster_destroy_spawned_exec_env(WASMExecEnv *exec_env) wasm_module_inst_t module_inst = wasm_runtime_get_module_inst(exec_env); bh_assert(cluster != NULL); - /* Free aux stack space */ - free_aux_stack(cluster, exec_env->aux_stack_bottom.bottom); - wasm_cluster_del_exec_env(cluster, exec_env); - wasm_exec_env_destroy_internal(exec_env); + os_mutex_lock(&cluster->lock); + /* Free aux stack space */ + free_aux_stack(exec_env, exec_env->aux_stack_bottom.bottom); + /* Remove exec_env */ + wasm_cluster_del_exec_env(cluster, exec_env); + /* Destroy exec_env */ + wasm_exec_env_destroy_internal(exec_env); + /* Routine exit, destroy instance */ wasm_runtime_deinstantiate_internal(module_inst, true); + + os_mutex_unlock(&cluster->lock); } /* start routine of thread manager */ @@ -457,27 +573,59 @@ thread_manager_start_routine(void *arg) void *ret; WASMExecEnv *exec_env = (WASMExecEnv *)arg; WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env); - bh_assert(cluster != NULL); + WASMModuleInstanceCommon *module_inst = + wasm_exec_env_get_module_inst(exec_env); + bh_assert(cluster != NULL); + bh_assert(module_inst != NULL); + + os_mutex_lock(&exec_env->wait_lock); exec_env->handle = os_self_thread(); + /* Notify the parent thread to continue running */ + os_cond_signal(&exec_env->wait_cond); + os_mutex_unlock(&exec_env->wait_lock); + ret = exec_env->thread_start_routine(exec_env); #ifdef OS_ENABLE_HW_BOUND_CHECK + os_mutex_lock(&exec_env->wait_lock); if (exec_env->suspend_flags.flags & 0x08) ret = exec_env->thread_ret_value; + os_mutex_unlock(&exec_env->wait_lock); #endif /* Routine exit */ - /* Free aux stack space */ - free_aux_stack(cluster, exec_env->aux_stack_bottom.bottom); - /* Detach the native thread here to ensure the resources are freed */ - wasm_cluster_detach_thread(exec_env); + #if WASM_ENABLE_DEBUG_INTERP != 0 wasm_cluster_thread_exited(exec_env); #endif - /* Remove and destroy exec_env */ + + os_mutex_lock(&cluster_list_lock); + + os_mutex_lock(&cluster->lock); + + /* Detach the native thread here to ensure the resources are freed */ + if (exec_env->wait_count == 0 && !exec_env->thread_is_detached) { + /* Only detach current thread when there is no other thread + joining it, otherwise let the system resources for the + thread be released after joining */ + os_thread_detach(exec_env->handle); + /* No need to set exec_env->thread_is_detached to true here + since we will exit soon */ + } + + /* Free aux stack space */ + free_aux_stack(exec_env, exec_env->aux_stack_bottom.bottom); + /* Remove exec_env */ wasm_cluster_del_exec_env(cluster, exec_env); + /* Destroy exec_env */ wasm_exec_env_destroy_internal(exec_env); + /* Routine exit, destroy instance */ + wasm_runtime_deinstantiate_internal(module_inst, true); + + os_mutex_unlock(&cluster->lock); + + os_mutex_unlock(&cluster_list_lock); os_thread_exit(ret); return ret; @@ -485,56 +633,83 @@ thread_manager_start_routine(void *arg) int32 wasm_cluster_create_thread(WASMExecEnv *exec_env, - wasm_module_inst_t module_inst, + wasm_module_inst_t module_inst, bool alloc_aux_stack, void *(*thread_routine)(void *), void *arg) { WASMCluster *cluster; WASMExecEnv *new_exec_env; - uint32 aux_stack_start, aux_stack_size; + uint32 aux_stack_start = 0, aux_stack_size; korp_tid tid; cluster = wasm_exec_env_get_cluster(exec_env); bh_assert(cluster); - new_exec_env = - wasm_exec_env_create_internal(module_inst, exec_env->wasm_stack_size); - if (!new_exec_env) - return -1; + os_mutex_lock(&cluster->lock); - if (!allocate_aux_stack(cluster, &aux_stack_start, &aux_stack_size)) { - LOG_ERROR("thread manager error: " - "failed to allocate aux stack space for new thread"); + if (cluster->has_exception || cluster->processing) { goto fail1; } - /* Set aux stack for current thread */ - if (!wasm_exec_env_set_aux_stack(new_exec_env, aux_stack_start, - aux_stack_size)) { - goto fail2; + new_exec_env = + wasm_exec_env_create_internal(module_inst, exec_env->wasm_stack_size); + if (!new_exec_env) + goto fail1; + + if (alloc_aux_stack) { + if (!allocate_aux_stack(exec_env, &aux_stack_start, &aux_stack_size)) { + LOG_ERROR("thread manager error: " + "failed to allocate aux stack space for new thread"); + goto fail2; + } + + /* Set aux stack for current thread */ + if (!wasm_exec_env_set_aux_stack(new_exec_env, aux_stack_start, + aux_stack_size)) { + goto fail3; + } + } + else { + /* Disable aux stack */ + new_exec_env->aux_stack_boundary.boundary = 0; + new_exec_env->aux_stack_bottom.bottom = UINT32_MAX; } if (!wasm_cluster_add_exec_env(cluster, new_exec_env)) - goto fail2; + goto fail3; new_exec_env->thread_start_routine = thread_routine; new_exec_env->thread_arg = arg; + os_mutex_lock(&new_exec_env->wait_lock); + if (0 != os_thread_create(&tid, thread_manager_start_routine, (void *)new_exec_env, APP_THREAD_STACK_SIZE_DEFAULT)) { - goto fail3; + os_mutex_unlock(&new_exec_env->wait_lock); + goto fail4; } + /* Wait until the new_exec_env->handle is set to avoid it is + illegally accessed after unlocking cluster->lock */ + os_cond_wait(&new_exec_env->wait_cond, &new_exec_env->wait_lock); + os_mutex_unlock(&new_exec_env->wait_lock); + + os_mutex_unlock(&cluster->lock); + return 0; -fail3: +fail4: wasm_cluster_del_exec_env(cluster, new_exec_env); -fail2: +fail3: /* free the allocated aux stack space */ - free_aux_stack(cluster, aux_stack_start); + if (alloc_aux_stack) + free_aux_stack(exec_env, aux_stack_start); +fail2: + wasm_exec_env_destroy_internal(new_exec_env); fail1: - wasm_exec_env_destroy(new_exec_env); + os_mutex_unlock(&cluster->lock); + return -1; } @@ -609,17 +784,17 @@ notify_debug_instance_exit(WASMExecEnv *exec_env) on_thread_exit_event(cluster->debug_inst, exec_env); } -void -wasm_cluster_thread_stopped(WASMExecEnv *exec_env) -{ - exec_env->current_status->running_status = STATUS_STOP; - notify_debug_instance(exec_env); -} - void wasm_cluster_thread_waiting_run(WASMExecEnv *exec_env) { os_mutex_lock(&exec_env->wait_lock); + + /* Wake up debugger thread after we get the lock, otherwise we may miss the + * signal from debugger thread, see + * https://github.com/bytecodealliance/wasm-micro-runtime/issues/1860 */ + exec_env->current_status->running_status = STATUS_STOP; + notify_debug_instance(exec_env); + while (!wasm_cluster_thread_is_running(exec_env)) { os_cond_wait(&exec_env->wait_cond, &exec_env->wait_lock); } @@ -646,16 +821,20 @@ wasm_cluster_thread_exited(WASMExecEnv *exec_env) void wasm_cluster_thread_continue(WASMExecEnv *exec_env) { + os_mutex_lock(&exec_env->wait_lock); wasm_cluster_clear_thread_signal(exec_env); exec_env->current_status->running_status = STATUS_RUNNING; os_cond_signal(&exec_env->wait_cond); + os_mutex_unlock(&exec_env->wait_lock); } void wasm_cluster_thread_step(WASMExecEnv *exec_env) { + os_mutex_lock(&exec_env->wait_lock); exec_env->current_status->running_status = STATUS_STEP; os_cond_signal(&exec_env->wait_cond); + os_mutex_unlock(&exec_env->wait_lock); } void @@ -675,15 +854,18 @@ clusters_have_exec_env(WASMExecEnv *exec_env) WASMExecEnv *node; while (cluster) { + os_mutex_lock(&cluster->lock); node = bh_list_first_elem(&cluster->exec_env_list); while (node) { if (node == exec_env) { bh_assert(exec_env->cluster == cluster); + os_mutex_unlock(&cluster->lock); return true; } node = bh_list_elem_next(node); } + os_mutex_unlock(&cluster->lock); cluster = bh_list_elem_next(cluster); } @@ -697,6 +879,7 @@ wasm_cluster_join_thread(WASMExecEnv *exec_env, void **ret_val) korp_tid handle; os_mutex_lock(&cluster_list_lock); + if (!clusters_have_exec_env(exec_env) || exec_env->thread_is_detached) { /* Invalid thread, thread has exited or thread has been detached */ if (ret_val) @@ -704,9 +887,14 @@ wasm_cluster_join_thread(WASMExecEnv *exec_env, void **ret_val) os_mutex_unlock(&cluster_list_lock); return 0; } + + os_mutex_lock(&exec_env->wait_lock); exec_env->wait_count++; handle = exec_env->handle; + os_mutex_unlock(&exec_env->wait_lock); + os_mutex_unlock(&cluster_list_lock); + return os_thread_join(handle, ret_val); } @@ -736,6 +924,7 @@ void wasm_cluster_exit_thread(WASMExecEnv *exec_env, void *retval) { WASMCluster *cluster; + WASMModuleInstanceCommon *module_inst; #ifdef OS_ENABLE_HW_BOUND_CHECK if (exec_env->jmpbuf_stack_top) { @@ -760,35 +949,74 @@ wasm_cluster_exit_thread(WASMExecEnv *exec_env, void *retval) wasm_cluster_clear_thread_signal(exec_env); wasm_cluster_thread_exited(exec_env); #endif + /* App exit the thread, free the resources before exit native thread */ - /* Free aux stack space */ - free_aux_stack(cluster, exec_env->aux_stack_bottom.bottom); + + os_mutex_lock(&cluster_list_lock); + + os_mutex_lock(&cluster->lock); + /* Detach the native thread here to ensure the resources are freed */ - wasm_cluster_detach_thread(exec_env); - /* Remove and destroy exec_env */ + if (exec_env->wait_count == 0 && !exec_env->thread_is_detached) { + /* Only detach current thread when there is no other thread + joining it, otherwise let the system resources for the + thread be released after joining */ + os_thread_detach(exec_env->handle); + /* No need to set exec_env->thread_is_detached to true here + since we will exit soon */ + } + + module_inst = exec_env->module_inst; + + /* Free aux stack space */ + free_aux_stack(exec_env, exec_env->aux_stack_bottom.bottom); + /* Remove exec_env */ wasm_cluster_del_exec_env(cluster, exec_env); + /* Destroy exec_env */ wasm_exec_env_destroy_internal(exec_env); + /* Routine exit, destroy instance */ + wasm_runtime_deinstantiate_internal(module_inst, true); + + os_mutex_unlock(&cluster->lock); + + os_mutex_unlock(&cluster_list_lock); os_thread_exit(retval); } -int32 -wasm_cluster_cancel_thread(WASMExecEnv *exec_env) +static void +set_thread_cancel_flags(WASMExecEnv *exec_env) { - os_mutex_lock(&cluster_list_lock); - if (!clusters_have_exec_env(exec_env)) { - /* Invalid thread or the thread has exited */ - os_mutex_unlock(&cluster_list_lock); - return 0; - } - os_mutex_unlock(&cluster_list_lock); - + os_mutex_lock(&exec_env->wait_lock); /* Set the termination flag */ #if WASM_ENABLE_DEBUG_INTERP != 0 wasm_cluster_thread_send_signal(exec_env, WAMR_SIG_TERM); #else exec_env->suspend_flags.flags |= 0x01; #endif + os_mutex_unlock(&exec_env->wait_lock); +} + +int32 +wasm_cluster_cancel_thread(WASMExecEnv *exec_env) +{ + os_mutex_lock(&cluster_list_lock); + + if (!exec_env->cluster) { + os_mutex_unlock(&cluster_list_lock); + return 0; + } + + if (!clusters_have_exec_env(exec_env)) { + /* Invalid thread or the thread has exited */ + goto final; + } + + set_thread_cancel_flags(exec_env); + +final: + os_mutex_unlock(&cluster_list_lock); + return 0; } @@ -808,15 +1036,27 @@ terminate_thread_visitor(void *node, void *user_data) void wasm_cluster_terminate_all(WASMCluster *cluster) { - traverse_list(&cluster->exec_env_list, terminate_thread_visitor, NULL); + os_mutex_lock(&cluster->lock); + cluster->processing = true; + + safe_traverse_exec_env_list(cluster, terminate_thread_visitor, NULL); + + cluster->processing = false; + os_mutex_unlock(&cluster->lock); } void wasm_cluster_terminate_all_except_self(WASMCluster *cluster, WASMExecEnv *exec_env) { - traverse_list(&cluster->exec_env_list, terminate_thread_visitor, - (void *)exec_env); + os_mutex_lock(&cluster->lock); + cluster->processing = true; + + safe_traverse_exec_env_list(cluster, terminate_thread_visitor, + (void *)exec_env); + + cluster->processing = false; + os_mutex_unlock(&cluster->lock); } static void @@ -834,15 +1074,27 @@ wait_for_thread_visitor(void *node, void *user_data) void wams_cluster_wait_for_all(WASMCluster *cluster) { - traverse_list(&cluster->exec_env_list, wait_for_thread_visitor, NULL); + os_mutex_lock(&cluster->lock); + cluster->processing = true; + + safe_traverse_exec_env_list(cluster, wait_for_thread_visitor, NULL); + + cluster->processing = false; + os_mutex_unlock(&cluster->lock); } void wasm_cluster_wait_for_all_except_self(WASMCluster *cluster, WASMExecEnv *exec_env) { - traverse_list(&cluster->exec_env_list, wait_for_thread_visitor, - (void *)exec_env); + os_mutex_lock(&cluster->lock); + cluster->processing = true; + + safe_traverse_exec_env_list(cluster, wait_for_thread_visitor, + (void *)exec_env); + + cluster->processing = false; + os_mutex_unlock(&cluster->lock); } bool @@ -881,15 +1133,19 @@ suspend_thread_visitor(void *node, void *user_data) void wasm_cluster_suspend_all(WASMCluster *cluster) { + os_mutex_lock(&cluster->lock); traverse_list(&cluster->exec_env_list, suspend_thread_visitor, NULL); + os_mutex_unlock(&cluster->lock); } void wasm_cluster_suspend_all_except_self(WASMCluster *cluster, WASMExecEnv *exec_env) { + os_mutex_lock(&cluster->lock); traverse_list(&cluster->exec_env_list, suspend_thread_visitor, (void *)exec_env); + os_mutex_unlock(&cluster->lock); } void @@ -910,7 +1166,9 @@ resume_thread_visitor(void *node, void *user_data) void wasm_cluster_resume_all(WASMCluster *cluster) { + os_mutex_lock(&cluster->lock); traverse_list(&cluster->exec_env_list, resume_thread_visitor, NULL); + os_mutex_unlock(&cluster->lock); } static void @@ -919,24 +1177,71 @@ set_exception_visitor(void *node, void *user_data) WASMExecEnv *curr_exec_env = (WASMExecEnv *)node; WASMExecEnv *exec_env = (WASMExecEnv *)user_data; WASMModuleInstanceCommon *module_inst = get_module_inst(exec_env); - WASMModuleInstanceCommon *curr_module_inst = get_module_inst(curr_exec_env); - const char *exception = wasm_runtime_get_exception(module_inst); - /* skip "Exception: " */ - exception += 11; + WASMModuleInstance *wasm_inst = (WASMModuleInstance *)module_inst; if (curr_exec_env != exec_env) { - curr_module_inst = get_module_inst(curr_exec_env); - wasm_runtime_set_exception(curr_module_inst, exception); + WASMModuleInstance *curr_wasm_inst = + (WASMModuleInstance *)get_module_inst(curr_exec_env); + + /* Only spread non "wasi proc exit" exception */ +#if WASM_ENABLE_SHARED_MEMORY != 0 + WASMSharedMemNode *shared_mem_node = wasm_module_get_shared_memory( + (WASMModuleCommon *)curr_wasm_inst->module); + if (shared_mem_node) + os_mutex_lock(&shared_mem_node->shared_mem_lock); +#endif + if (!strstr(wasm_inst->cur_exception, "wasi proc exit")) { + bh_memcpy_s(curr_wasm_inst->cur_exception, + sizeof(curr_wasm_inst->cur_exception), + wasm_inst->cur_exception, + sizeof(wasm_inst->cur_exception)); + } +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (shared_mem_node) + os_mutex_unlock(&shared_mem_node->shared_mem_lock); +#endif + + /* Terminate the thread so it can exit from dead loops */ + set_thread_cancel_flags(curr_exec_env); + } +} + +static void +clear_exception_visitor(void *node, void *user_data) +{ + WASMExecEnv *exec_env = (WASMExecEnv *)user_data; + WASMExecEnv *curr_exec_env = (WASMExecEnv *)node; + + if (curr_exec_env != exec_env) { + WASMModuleInstance *curr_wasm_inst = + (WASMModuleInstance *)get_module_inst(curr_exec_env); + +#if WASM_ENABLE_SHARED_MEMORY != 0 + WASMSharedMemNode *shared_mem_node = wasm_module_get_shared_memory( + (WASMModuleCommon *)curr_wasm_inst->module); + if (shared_mem_node) + os_mutex_lock(&shared_mem_node->shared_mem_lock); +#endif + curr_wasm_inst->cur_exception[0] = '\0'; +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (shared_mem_node) + os_mutex_unlock(&shared_mem_node->shared_mem_lock); +#endif } } void -wasm_cluster_spread_exception(WASMExecEnv *exec_env) +wasm_cluster_spread_exception(WASMExecEnv *exec_env, bool clear) { WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env); bh_assert(cluster); - traverse_list(&cluster->exec_env_list, set_exception_visitor, exec_env); + os_mutex_lock(&cluster->lock); + cluster->has_exception = !clear; + traverse_list(&cluster->exec_env_list, + clear ? clear_exception_visitor : set_exception_visitor, + exec_env); + os_mutex_unlock(&cluster->lock); } static void @@ -964,7 +1269,20 @@ wasm_cluster_spread_custom_data(WASMModuleInstanceCommon *module_inst, cluster = wasm_exec_env_get_cluster(exec_env); bh_assert(cluster); + os_mutex_lock(&cluster->lock); traverse_list(&cluster->exec_env_list, set_custom_data_visitor, custom_data); + os_mutex_unlock(&cluster->lock); } } + +bool +wasm_cluster_is_thread_terminated(WASMExecEnv *exec_env) +{ + os_mutex_lock(&exec_env->wait_lock); + bool is_thread_terminated = + (exec_env->suspend_flags.flags & 0x01) ? true : false; + os_mutex_unlock(&exec_env->wait_lock); + + return is_thread_terminated; +} diff --git a/core/iwasm/libraries/thread-mgr/thread_manager.h b/core/iwasm/libraries/thread-mgr/thread_manager.h index c84d3c021..38ca167fb 100644 --- a/core/iwasm/libraries/thread-mgr/thread_manager.h +++ b/core/iwasm/libraries/thread-mgr/thread_manager.h @@ -26,14 +26,28 @@ struct WASMCluster { korp_mutex lock; bh_list exec_env_list; +#if WASM_ENABLE_HEAP_AUX_STACK_ALLOCATION == 0 /* The aux stack of a module with shared memory will be divided into several segments. This array store the stack top of different segments */ uint32 *stack_tops; - /* Size of every stack segment */ - uint32 stack_size; /* Record which segments are occupied */ bool *stack_segment_occupied; +#endif + /* Size of every stack segment */ + uint32 stack_size; + /* When has_exception == true, this cluster should refuse any spawn thread + * requests, this flag can be cleared by calling + * wasm_runtime_clear_exception on instances of any threads of this cluster + */ + bool has_exception; + /* When processing is true, this cluster should refuse any spawn thread + * requests. This is a short-lived state, must be cleared immediately once + * the processing finished. + * This is used to avoid dead lock when one thread waiting another thread + * with lock, see wams_cluster_wait_for_all and wasm_cluster_terminate_all + */ + bool processing; #if WASM_ENABLE_DEBUG_INTERP != 0 WASMDebugInstance *debug_inst; #endif @@ -62,7 +76,7 @@ wasm_exec_env_get_cluster(WASMExecEnv *exec_env); int32 wasm_cluster_create_thread(WASMExecEnv *exec_env, - wasm_module_inst_t module_inst, + wasm_module_inst_t module_inst, bool alloc_aux_stack, void *(*thread_routine)(void *), void *arg); int32 @@ -113,9 +127,6 @@ void wasm_cluster_wait_for_all_except_self(WASMCluster *cluster, WASMExecEnv *exec_env); -bool -wasm_cluster_add_exec_env(WASMCluster *cluster, WASMExecEnv *exec_env); - bool wasm_cluster_del_exec_env(WASMCluster *cluster, WASMExecEnv *exec_env); @@ -123,7 +134,7 @@ WASMExecEnv * wasm_clusters_search_exec_env(WASMModuleInstanceCommon *module_inst); void -wasm_cluster_spread_exception(WASMExecEnv *exec_env); +wasm_cluster_spread_exception(WASMExecEnv *exec_env, bool clear); WASMExecEnv * wasm_cluster_spawn_exec_env(WASMExecEnv *exec_env); @@ -135,6 +146,9 @@ void wasm_cluster_spread_custom_data(WASMModuleInstanceCommon *module_inst, void *custom_data); +bool +wasm_cluster_is_thread_terminated(WASMExecEnv *exec_env); + #if WASM_ENABLE_DEBUG_INTERP != 0 #define WAMR_SIG_TRAP (5) #define WAMR_SIG_STOP (19) @@ -166,9 +180,6 @@ wasm_cluster_destroy_exenv_status(WASMCurrentEnvStatus *status); void wasm_cluster_send_signal_all(WASMCluster *cluster, uint32 signo); -void -wasm_cluster_thread_stopped(WASMExecEnv *exec_env); - void wasm_cluster_thread_waiting_run(WASMExecEnv *exec_env); diff --git a/core/iwasm/libraries/wasi-nn/.dockerignore b/core/iwasm/libraries/wasi-nn/.dockerignore deleted file mode 100644 index 0e2be498d..000000000 --- a/core/iwasm/libraries/wasi-nn/.dockerignore +++ /dev/null @@ -1 +0,0 @@ -**/Dockerfile diff --git a/core/iwasm/libraries/wasi-nn/README.md b/core/iwasm/libraries/wasi-nn/README.md index 22ef13db0..c5762618d 100644 --- a/core/iwasm/libraries/wasi-nn/README.md +++ b/core/iwasm/libraries/wasi-nn/README.md @@ -17,27 +17,67 @@ By only including this file in your WASM application you will bind WASI-NN into To run the tests we assume that the current directory is the root of the repository. -1. Build the docker image, +### Build the runtime + +Build the runtime image for your execution target type. + +`EXECUTION_TYPE` can be: +* `cpu` +* `nvidia-gpu` ``` -docker build -t wasi-nn -f core/iwasm/libraries/wasi-nn/test/Dockerfile . +EXECUTION_TYPE=cpu +docker build -t wasi-nn-${EXECUTION_TYPE} -f core/iwasm/libraries/wasi-nn/test/Dockerfile.${EXECUTION_TYPE} . ``` -2. Run the container + +### Build wasm app ``` -docker run wasi-nn +docker build -t wasi-nn-compile -f core/iwasm/libraries/wasi-nn/test/Dockerfile.compile . ``` +``` +docker run -v $PWD/core/iwasm/libraries/wasi-nn:/wasi-nn wasi-nn-compile +``` + + +### Run wasm app + If all the tests have run properly you will the the following message in the terminal, ``` Tests: passed! ``` +* CPU + +``` +docker run \ + -v $PWD/core/iwasm/libraries/wasi-nn/test:/assets wasi-nn-cpu \ + --dir=/assets \ + --env="TARGET=cpu" \ + /assets/test_tensorflow.wasm +``` + +* (NVIDIA) GPU + +``` +docker run \ + --runtime=nvidia \ + -v $PWD/core/iwasm/libraries/wasi-nn/test:/assets wasi-nn-nvidia-gpu \ + --dir=/assets \ + --env="TARGET=gpu" \ + /assets/test_tensorflow.wasm +``` + +Requirements: +* [NVIDIA docker](https://github.com/NVIDIA/nvidia-docker). + ## What is missing -* Only 1 model at a time is supported. - * `graph` and `graph-execution-context` are ignored. -* Only `tensorflow` (lite) is supported. -* Only `cpu` is supported. +Supported: + +* Graph encoding: `tensorflowlite`. +* Execution target: `cpu` and `gpu`. +* Tensor type: `fp32`. diff --git a/core/iwasm/libraries/wasi-nn/logger.h b/core/iwasm/libraries/wasi-nn/logger.h deleted file mode 100644 index 25588eb6b..000000000 --- a/core/iwasm/libraries/wasi-nn/logger.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef WASI_NN_LOGGER_H -#define WASI_NN_LOGGER_H - -#include -#include - -#define __FILENAME__ \ - (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) - -/* Disable a level by removing the define */ -#define ENABLE_ERR_LOG -#define ENABLE_WARN_LOG -#define ENABLE_DBG_LOG -#define ENABLE_INFO_LOG - -// Definition of the levels -#ifdef ENABLE_ERR_LOG -#define NN_ERR_PRINTF(fmt, ...) \ - printf("[%s:%d] " fmt, __FILENAME__, __LINE__, ##__VA_ARGS__); \ - printf("\n"); \ - fflush(stdout) -#else -#define NN_ERR_PRINTF(fmt, ...) -#endif -#ifdef ENABLE_WARN_LOG -#define NN_WARN_PRINTF(fmt, ...) \ - printf("[%s:%d] " fmt, __FILENAME__, __LINE__, ##__VA_ARGS__); \ - printf("\n"); \ - fflush(stdout) -#else -#define NN_WARN_PRINTF(fmt, ...) -#endif -#ifdef ENABLE_DBG_LOG -#define NN_DBG_PRINTF(fmt, ...) \ - printf("[%s:%d] " fmt, __FILENAME__, __LINE__, ##__VA_ARGS__); \ - printf("\n"); \ - fflush(stdout) -#else -#define NN_DBG_PRINTF(fmt, ...) -#endif -#ifdef ENABLE_INFO_LOG -#define NN_INFO_PRINTF(fmt, ...) \ - printf("[%s:%d] " fmt, __FILENAME__, __LINE__, ##__VA_ARGS__); \ - printf("\n"); \ - fflush(stdout) -#else -#define NN_INFO_PRINTF(fmt, ...) -#endif - -#endif diff --git a/core/iwasm/libraries/wasi-nn/src/utils/logger.h b/core/iwasm/libraries/wasi-nn/src/utils/logger.h new file mode 100644 index 000000000..a196429a0 --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/src/utils/logger.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef WASI_NN_LOGGER_H +#define WASI_NN_LOGGER_H + +#include +#include + +#define __FILENAME__ \ + (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) + +/* Disable a level by removing the define */ +#ifndef NN_LOG_LEVEL +/* + 0 -> debug, info, warn, err + 1 -> info, warn, err + 2 -> warn, err + 3 -> err + 4 -> NO LOGS +*/ +#define NN_LOG_LEVEL 0 +#endif + +// Definition of the levels +#if NN_LOG_LEVEL <= 3 +#define NN_ERR_PRINTF(fmt, ...) \ + do { \ + printf("[%s:%d ERROR] " fmt, __FILENAME__, __LINE__, ##__VA_ARGS__); \ + printf("\n"); \ + fflush(stdout); \ + } while (0) +#else +#define NN_ERR_PRINTF(fmt, ...) +#endif +#if NN_LOG_LEVEL <= 2 +#define NN_WARN_PRINTF(fmt, ...) \ + do { \ + printf("[%s:%d WARNING] " fmt, __FILENAME__, __LINE__, ##__VA_ARGS__); \ + printf("\n"); \ + fflush(stdout); \ + } while (0) +#else +#define NN_WARN_PRINTF(fmt, ...) +#endif +#if NN_LOG_LEVEL <= 1 +#define NN_INFO_PRINTF(fmt, ...) \ + do { \ + printf("[%s:%d INFO] " fmt, __FILENAME__, __LINE__, ##__VA_ARGS__); \ + printf("\n"); \ + fflush(stdout); \ + } while (0) +#else +#define NN_INFO_PRINTF(fmt, ...) +#endif +#if NN_LOG_LEVEL <= 0 +#define NN_DBG_PRINTF(fmt, ...) \ + do { \ + printf("[%s:%d DEBUG] " fmt, __FILENAME__, __LINE__, ##__VA_ARGS__); \ + printf("\n"); \ + fflush(stdout); \ + } while (0) +#else +#define NN_DBG_PRINTF(fmt, ...) +#endif + +#endif diff --git a/core/iwasm/libraries/wasi-nn/src/utils/wasi_nn_app_native.c b/core/iwasm/libraries/wasi-nn/src/utils/wasi_nn_app_native.c new file mode 100644 index 000000000..fe04b657b --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/src/utils/wasi_nn_app_native.c @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasi_nn_app_native.h" + +static error +graph_builder_app_native(wasm_module_inst_t instance, + graph_builder_wasm *builder_wasm, + graph_builder *builder) +{ + if (!wasm_runtime_validate_app_addr(instance, builder_wasm->buf_offset, + builder_wasm->size * sizeof(uint8_t))) { + NN_ERR_PRINTF("builder_wasm->buf_offset is invalid"); + return invalid_argument; + } + + builder->buf = (uint8_t *)wasm_runtime_addr_app_to_native( + instance, builder_wasm->buf_offset); + builder->size = builder_wasm->size; + return success; +} + +error +graph_builder_array_app_native(wasm_module_inst_t instance, + graph_builder_array_wasm *builder_array_wasm, + graph_builder_array *builder_array) +{ + if (!wasm_runtime_validate_native_addr(instance, builder_array_wasm, + sizeof(graph_builder_array_wasm))) { + NN_ERR_PRINTF("builder_array_wasm is invalid"); + return invalid_argument; + } + + NN_DBG_PRINTF("Graph builder array contains %d elements", + builder_array_wasm->size); + + if (!wasm_runtime_validate_app_addr( + instance, builder_array_wasm->buf_offset, + builder_array_wasm->size * sizeof(graph_builder_wasm))) { + NN_ERR_PRINTF("builder_array_wasm->buf_offset is invalid"); + return invalid_argument; + } + + graph_builder_wasm *builder_wasm = + (graph_builder_wasm *)wasm_runtime_addr_app_to_native( + instance, builder_array_wasm->buf_offset); + + graph_builder *builder = (graph_builder *)wasm_runtime_malloc( + builder_array_wasm->size * sizeof(graph_builder)); + if (builder == NULL) + return missing_memory; + + for (uint32_t i = 0; i < builder_array_wasm->size; ++i) { + error res; + if (success + != (res = graph_builder_app_native(instance, &builder_wasm[i], + &builder[i]))) { + wasm_runtime_free(builder); + return res; + } + + NN_DBG_PRINTF("Graph builder %d contains %d elements", i, + builder->size); + } + + builder_array->buf = builder; + builder_array->size = builder_array_wasm->size; + return success; +} + +static error +tensor_data_app_native(wasm_module_inst_t instance, uint32_t total_elements, + tensor_wasm *input_tensor_wasm, tensor_data *data) +{ + if (!wasm_runtime_validate_app_addr( + instance, input_tensor_wasm->data_offset, total_elements)) { + NN_ERR_PRINTF("input_tensor_wasm->data_offset is invalid"); + return invalid_argument; + } + *data = (tensor_data)wasm_runtime_addr_app_to_native( + instance, input_tensor_wasm->data_offset); + return success; +} + +static error +tensor_dimensions_app_native(wasm_module_inst_t instance, + tensor_wasm *input_tensor_wasm, + tensor_dimensions **dimensions) +{ + if (!wasm_runtime_validate_app_addr(instance, + input_tensor_wasm->dimensions_offset, + sizeof(tensor_dimensions_wasm))) { + NN_ERR_PRINTF("input_tensor_wasm->dimensions_offset is invalid"); + return invalid_argument; + } + + tensor_dimensions_wasm *dimensions_wasm = + (tensor_dimensions_wasm *)wasm_runtime_addr_app_to_native( + instance, input_tensor_wasm->dimensions_offset); + + if (!wasm_runtime_validate_app_addr(instance, dimensions_wasm->buf_offset, + sizeof(tensor_dimensions))) { + NN_ERR_PRINTF("dimensions_wasm->buf_offset is invalid"); + return invalid_argument; + } + + *dimensions = + (tensor_dimensions *)wasm_runtime_malloc(sizeof(tensor_dimensions)); + if (dimensions == NULL) + return missing_memory; + + (*dimensions)->size = dimensions_wasm->size; + (*dimensions)->buf = (uint32_t *)wasm_runtime_addr_app_to_native( + instance, dimensions_wasm->buf_offset); + + NN_DBG_PRINTF("Number of dimensions: %d", (*dimensions)->size); + return success; +} + +error +tensor_app_native(wasm_module_inst_t instance, tensor_wasm *input_tensor_wasm, + tensor *input_tensor) +{ + NN_DBG_PRINTF("Converting tensor_wasm to tensor"); + if (!wasm_runtime_validate_native_addr(instance, input_tensor_wasm, + sizeof(tensor_wasm))) { + NN_ERR_PRINTF("input_tensor_wasm is invalid"); + return invalid_argument; + } + + error res; + + tensor_dimensions *dimensions = NULL; + if (success + != (res = tensor_dimensions_app_native(instance, input_tensor_wasm, + &dimensions))) { + NN_ERR_PRINTF("error when parsing dimensions"); + return res; + } + + uint32_t total_elements = 1; + for (uint32_t i = 0; i < dimensions->size; ++i) { + total_elements *= dimensions->buf[i]; + NN_DBG_PRINTF("Dimension %d: %d", i, dimensions->buf[i]); + } + NN_DBG_PRINTF("Tensor type: %d", input_tensor_wasm->type); + NN_DBG_PRINTF("Total number of elements: %d", total_elements); + + tensor_data data = NULL; + if (success + != (res = tensor_data_app_native(instance, total_elements, + input_tensor_wasm, &data))) { + wasm_runtime_free(dimensions); + return res; + } + + input_tensor->type = input_tensor_wasm->type; + input_tensor->dimensions = dimensions; + input_tensor->data = data; + return success; +} diff --git a/core/iwasm/libraries/wasi-nn/src/utils/wasi_nn_app_native.h b/core/iwasm/libraries/wasi-nn/src/utils/wasi_nn_app_native.h new file mode 100644 index 000000000..15154bd31 --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/src/utils/wasi_nn_app_native.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef WASI_NN_APP_NATIVE +#define WASI_NN_APP_NATIVE + +#include +#include +#include +#include +#include + +#include "wasi_nn.h" +#include "logger.h" + +#include "bh_platform.h" +#include "wasm_export.h" + +typedef struct { + uint32_t buf_offset; + uint32_t size; +} graph_builder_wasm; + +typedef struct { + uint32_t buf_offset; + uint32_t size; +} graph_builder_array_wasm; + +typedef struct { + uint32_t buf_offset; + uint32_t size; +} tensor_dimensions_wasm; + +typedef struct { + uint32_t dimensions_offset; + tensor_type type; + uint32_t data_offset; +} tensor_wasm; + +error +graph_builder_array_app_native(wasm_module_inst_t instance, + graph_builder_array_wasm *builder, + graph_builder_array *builder_native); + +error +tensor_app_native(wasm_module_inst_t instance, tensor_wasm *input_tensor, + tensor *input_tensor_native); + +#endif diff --git a/core/iwasm/libraries/wasi-nn/src/wasi_nn.c b/core/iwasm/libraries/wasi-nn/src/wasi_nn.c new file mode 100644 index 000000000..466630f99 --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/src/wasi_nn.c @@ -0,0 +1,306 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include +#include +#include +#include + +#include "wasi_nn.h" +#include "wasi_nn_app_native.h" +#include "logger.h" +#include "wasi_nn_tensorflowlite.hpp" + +#include "bh_platform.h" +#include "wasm_export.h" +#include "wasm_runtime.h" +#include "aot_runtime.h" + +/* Definition of 'wasi_nn.h' structs in WASM app format (using offset) */ + +typedef error (*LOAD)(void *, graph_builder_array *, graph_encoding, + execution_target, graph *); +typedef error (*INIT_EXECUTION_CONTEXT)(void *, graph, + graph_execution_context *); +typedef error (*SET_INPUT)(void *, graph_execution_context, uint32_t, tensor *); +typedef error (*COMPUTE)(void *, graph_execution_context); +typedef error (*GET_OUTPUT)(void *, graph_execution_context, uint32_t, + tensor_data, uint32_t *); + +typedef struct { + LOAD load; + INIT_EXECUTION_CONTEXT init_execution_context; + SET_INPUT set_input; + COMPUTE compute; + GET_OUTPUT get_output; +} api_function; + +/* Global variables */ + +static api_function lookup[] = { + { NULL, NULL, NULL, NULL, NULL }, + { NULL, NULL, NULL, NULL, NULL }, + { NULL, NULL, NULL, NULL, NULL }, + { NULL, NULL, NULL, NULL, NULL }, + { tensorflowlite_load, tensorflowlite_init_execution_context, + tensorflowlite_set_input, tensorflowlite_compute, + tensorflowlite_get_output } +}; + +/* Utils */ + +static bool +is_encoding_implemented(graph_encoding encoding) +{ + return lookup[encoding].load && lookup[encoding].init_execution_context + && lookup[encoding].set_input && lookup[encoding].compute + && lookup[encoding].get_output; +} + +static error +is_model_initialized(WASINNContext *wasi_nn_ctx) +{ + if (!wasi_nn_ctx->is_initialized) { + NN_ERR_PRINTF("Model not initialized."); + return runtime_error; + } + return success; +} + +WASINNContext * +wasm_runtime_get_wasi_nn_ctx(wasm_module_inst_t instance) +{ + WASINNContext *wasi_nn_ctx = NULL; +#if WASM_ENABLE_INTERP != 0 + if (instance->module_type == Wasm_Module_Bytecode) { + NN_DBG_PRINTF("Getting ctx from WASM"); + WASMModuleInstance *module_inst = (WASMModuleInstance *)instance; + wasi_nn_ctx = ((WASMModuleInstanceExtra *)module_inst->e)->wasi_nn_ctx; + } +#endif +#if WASM_ENABLE_AOT != 0 + if (instance->module_type == Wasm_Module_AoT) { + NN_DBG_PRINTF("Getting ctx from AOT"); + AOTModuleInstance *module_inst = (AOTModuleInstance *)instance; + wasi_nn_ctx = ((AOTModuleInstanceExtra *)module_inst->e)->wasi_nn_ctx; + } +#endif + bh_assert(wasi_nn_ctx != NULL); + NN_DBG_PRINTF("Returning ctx"); + return wasi_nn_ctx; +} + +/* WASI-NN implementation */ + +error +wasi_nn_load(wasm_exec_env_t exec_env, graph_builder_array_wasm *builder, + graph_encoding encoding, execution_target target, graph *g) +{ + NN_DBG_PRINTF("Running wasi_nn_load [encoding=%d, target=%d]...", encoding, + target); + + if (!is_encoding_implemented(encoding)) { + NN_ERR_PRINTF("Encoding not supported."); + return invalid_encoding; + } + + wasm_module_inst_t instance = wasm_runtime_get_module_inst(exec_env); + bh_assert(instance); + + error res; + graph_builder_array builder_native = { 0 }; + if (success + != (res = graph_builder_array_app_native(instance, builder, + &builder_native))) + return res; + + if (!wasm_runtime_validate_native_addr(instance, g, sizeof(graph))) { + NN_ERR_PRINTF("graph is invalid"); + res = invalid_argument; + goto fail; + } + + WASINNContext *wasi_nn_ctx = wasm_runtime_get_wasi_nn_ctx(instance); + res = lookup[encoding].load(wasi_nn_ctx->tflite_ctx, &builder_native, + encoding, target, g); + + NN_DBG_PRINTF("wasi_nn_load finished with status %d [graph=%d]", res, *g); + + wasi_nn_ctx->current_encoding = encoding; + wasi_nn_ctx->is_initialized = true; + +fail: + // XXX: Free intermediate structure pointers + if (builder_native.buf) + wasm_runtime_free(builder_native.buf); + + return res; +} + +error +wasi_nn_init_execution_context(wasm_exec_env_t exec_env, graph g, + graph_execution_context *ctx) +{ + NN_DBG_PRINTF("Running wasi_nn_init_execution_context [graph=%d]...", g); + + wasm_module_inst_t instance = wasm_runtime_get_module_inst(exec_env); + bh_assert(instance); + WASINNContext *wasi_nn_ctx = wasm_runtime_get_wasi_nn_ctx(instance); + + error res; + if (success != (res = is_model_initialized(wasi_nn_ctx))) + return res; + + if (!wasm_runtime_validate_native_addr(instance, ctx, + sizeof(graph_execution_context))) { + NN_ERR_PRINTF("ctx is invalid"); + return invalid_argument; + } + + res = lookup[wasi_nn_ctx->current_encoding].init_execution_context( + wasi_nn_ctx->tflite_ctx, g, ctx); + + NN_DBG_PRINTF( + "wasi_nn_init_execution_context finished with status %d [ctx=%d]", res, + *ctx); + return res; +} + +error +wasi_nn_set_input(wasm_exec_env_t exec_env, graph_execution_context ctx, + uint32_t index, tensor_wasm *input_tensor) +{ + NN_DBG_PRINTF("Running wasi_nn_set_input [ctx=%d, index=%d]...", ctx, + index); + + wasm_module_inst_t instance = wasm_runtime_get_module_inst(exec_env); + bh_assert(instance); + WASINNContext *wasi_nn_ctx = wasm_runtime_get_wasi_nn_ctx(instance); + + error res; + if (success != (res = is_model_initialized(wasi_nn_ctx))) + return res; + + tensor input_tensor_native = { 0 }; + if (success + != (res = tensor_app_native(instance, input_tensor, + &input_tensor_native))) + return res; + + res = lookup[wasi_nn_ctx->current_encoding].set_input( + wasi_nn_ctx->tflite_ctx, ctx, index, &input_tensor_native); + + // XXX: Free intermediate structure pointers + if (input_tensor_native.dimensions) + wasm_runtime_free(input_tensor_native.dimensions); + + NN_DBG_PRINTF("wasi_nn_set_input finished with status %d", res); + return res; +} + +error +wasi_nn_compute(wasm_exec_env_t exec_env, graph_execution_context ctx) +{ + NN_DBG_PRINTF("Running wasi_nn_compute [ctx=%d]...", ctx); + + wasm_module_inst_t instance = wasm_runtime_get_module_inst(exec_env); + bh_assert(instance); + WASINNContext *wasi_nn_ctx = wasm_runtime_get_wasi_nn_ctx(instance); + + error res; + if (success != (res = is_model_initialized(wasi_nn_ctx))) + return res; + + res = lookup[wasi_nn_ctx->current_encoding].compute(wasi_nn_ctx->tflite_ctx, + ctx); + NN_DBG_PRINTF("wasi_nn_compute finished with status %d", res); + return res; +} + +error +wasi_nn_get_output(wasm_exec_env_t exec_env, graph_execution_context ctx, + uint32_t index, tensor_data output_tensor, + uint32_t *output_tensor_size) +{ + NN_DBG_PRINTF("Running wasi_nn_get_output [ctx=%d, index=%d]...", ctx, + index); + + wasm_module_inst_t instance = wasm_runtime_get_module_inst(exec_env); + bh_assert(instance); + WASINNContext *wasi_nn_ctx = wasm_runtime_get_wasi_nn_ctx(instance); + + error res; + if (success != (res = is_model_initialized(wasi_nn_ctx))) + return res; + + if (!wasm_runtime_validate_native_addr(instance, output_tensor_size, + sizeof(uint32_t))) { + NN_ERR_PRINTF("output_tensor_size is invalid"); + return invalid_argument; + } + + res = lookup[wasi_nn_ctx->current_encoding].get_output( + wasi_nn_ctx->tflite_ctx, ctx, index, output_tensor, output_tensor_size); + NN_DBG_PRINTF("wasi_nn_get_output finished with status %d [data_size=%d]", + res, *output_tensor_size); + return res; +} + +/* Non-exposed public functions */ + +WASINNContext * +wasi_nn_initialize() +{ + NN_DBG_PRINTF("Initializing wasi-nn"); + WASINNContext *wasi_nn_ctx = + (WASINNContext *)wasm_runtime_malloc(sizeof(WASINNContext)); + if (wasi_nn_ctx == NULL) { + NN_ERR_PRINTF("Error when allocating memory for WASI-NN context"); + return NULL; + } + wasi_nn_ctx->is_initialized = true; + wasi_nn_ctx->current_encoding = 3; + tensorflowlite_initialize(&wasi_nn_ctx->tflite_ctx); + return wasi_nn_ctx; +} + +void +wasi_nn_destroy(WASINNContext *wasi_nn_ctx) +{ + if (wasi_nn_ctx == NULL) { + NN_ERR_PRINTF( + "Error when deallocating memory. WASI-NN context is NULL"); + return; + } + NN_DBG_PRINTF("Freeing wasi-nn"); + NN_DBG_PRINTF("-> is_initialized: %d", wasi_nn_ctx->is_initialized); + NN_DBG_PRINTF("-> current_encoding: %d", wasi_nn_ctx->current_encoding); + tensorflowlite_destroy(wasi_nn_ctx->tflite_ctx); + wasm_runtime_free(wasi_nn_ctx); +} + +/* Register WASI-NN in WAMR */ + +/* clang-format off */ +#define REG_NATIVE_FUNC(func_name, signature) \ + { #func_name, wasi_nn_##func_name, signature, NULL } +/* clang-format on */ + +static NativeSymbol native_symbols_wasi_nn[] = { + REG_NATIVE_FUNC(load, "(*ii*)i"), + REG_NATIVE_FUNC(init_execution_context, "(i*)i"), + REG_NATIVE_FUNC(set_input, "(ii*)i"), + REG_NATIVE_FUNC(compute, "(i)i"), + REG_NATIVE_FUNC(get_output, "(ii**)i"), +}; + +uint32_t +get_wasi_nn_export_apis(NativeSymbol **p_libc_wasi_apis) +{ + *p_libc_wasi_apis = native_symbols_wasi_nn; + return sizeof(native_symbols_wasi_nn) / sizeof(NativeSymbol); +} diff --git a/core/iwasm/libraries/wasi-nn/src/wasi_nn_private.h b/core/iwasm/libraries/wasi-nn/src/wasi_nn_private.h new file mode 100644 index 000000000..52d16bd1d --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/src/wasi_nn_private.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef WASI_NN_PRIVATE_H +#define WASI_NN_PRIVATE_H + +#include "wasi_nn_types.h" + +typedef struct { + bool is_initialized; + graph_encoding current_encoding; + void *tflite_ctx; +} WASINNContext; + +/** + * @brief Initialize wasi-nn + * + */ +WASINNContext * +wasi_nn_initialize(); +/** + * @brief Destroy wasi-nn on app exists + * + */ + +void +wasi_nn_destroy(WASINNContext *wasi_nn_ctx); + +#endif diff --git a/core/iwasm/libraries/wasi-nn/src/wasi_nn_tensorflowlite.cpp b/core/iwasm/libraries/wasi-nn/src/wasi_nn_tensorflowlite.cpp new file mode 100644 index 000000000..0fe156381 --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/src/wasi_nn_tensorflowlite.cpp @@ -0,0 +1,380 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasi_nn.h" +#include "wasi_nn_tensorflowlite.hpp" +#include "logger.h" + +#include "bh_common.h" +#include "bh_platform.h" +#include "platform_common.h" + +#include +#include +#include +#include +#include + +#if defined(WASI_NN_ENABLE_GPU) +#include +#endif + +/* Maximum number of graphs per WASM instance */ +#define MAX_GRAPHS_PER_INST 10 +/* Maximum number of graph execution context per WASM instance*/ +#define MAX_GRAPH_EXEC_CONTEXTS_PER_INST 10 + +typedef struct { + std::unique_ptr interpreter; +} Interpreter; + +typedef struct { + char *model_pointer; + std::unique_ptr model; + execution_target target; +} Model; + +typedef struct { + uint32_t current_models; + Model models[MAX_GRAPHS_PER_INST]; + uint32_t current_interpreters; + Interpreter interpreters[MAX_GRAPH_EXEC_CONTEXTS_PER_INST]; + korp_mutex g_lock; +} TFLiteContext; + +/* Utils */ + +static error +initialize_g(TFLiteContext *tfl_ctx, graph *g) +{ + os_mutex_lock(&tfl_ctx->g_lock); + if (tfl_ctx->current_models == MAX_GRAPHS_PER_INST) { + os_mutex_unlock(&tfl_ctx->g_lock); + NN_ERR_PRINTF("Excedded max graphs per WASM instance"); + return runtime_error; + } + *g = tfl_ctx->current_models++; + os_mutex_unlock(&tfl_ctx->g_lock); + return success; +} +static error +initialize_graph_ctx(TFLiteContext *tfl_ctx, graph g, + graph_execution_context *ctx) +{ + os_mutex_lock(&tfl_ctx->g_lock); + if (tfl_ctx->current_interpreters == MAX_GRAPH_EXEC_CONTEXTS_PER_INST) { + os_mutex_unlock(&tfl_ctx->g_lock); + NN_ERR_PRINTF("Excedded max graph execution context per WASM instance"); + return runtime_error; + } + *ctx = tfl_ctx->current_interpreters++; + os_mutex_unlock(&tfl_ctx->g_lock); + return success; +} + +static error +is_valid_graph(TFLiteContext *tfl_ctx, graph g) +{ + if (g >= MAX_GRAPHS_PER_INST) { + NN_ERR_PRINTF("Invalid graph: %d >= %d.", g, MAX_GRAPHS_PER_INST); + return runtime_error; + } + if (tfl_ctx->models[g].model_pointer == NULL) { + NN_ERR_PRINTF("Context (model) non-initialized."); + return runtime_error; + } + if (tfl_ctx->models[g].model == NULL) { + NN_ERR_PRINTF("Context (tflite model) non-initialized."); + return runtime_error; + } + return success; +} + +static error +is_valid_graph_execution_context(TFLiteContext *tfl_ctx, + graph_execution_context ctx) +{ + if (ctx >= MAX_GRAPH_EXEC_CONTEXTS_PER_INST) { + NN_ERR_PRINTF("Invalid graph execution context: %d >= %d", ctx, + MAX_GRAPH_EXEC_CONTEXTS_PER_INST); + return runtime_error; + } + if (tfl_ctx->interpreters[ctx].interpreter == NULL) { + NN_ERR_PRINTF("Context (interpreter) non-initialized."); + return runtime_error; + } + return success; +} + +/* WASI-NN (tensorflow) implementation */ + +error +tensorflowlite_load(void *tflite_ctx, graph_builder_array *builder, + graph_encoding encoding, execution_target target, graph *g) +{ + TFLiteContext *tfl_ctx = (TFLiteContext *)tflite_ctx; + + if (builder->size != 1) { + NN_ERR_PRINTF("Unexpected builder format."); + return invalid_argument; + } + + if (encoding != tensorflowlite) { + NN_ERR_PRINTF("Encoding is not tensorflowlite."); + return invalid_argument; + } + + if (target != cpu && target != gpu) { + NN_ERR_PRINTF("Only CPU and GPU target is supported."); + return invalid_argument; + } + + error res; + if (success != (res = initialize_g(tfl_ctx, g))) + return res; + + uint32_t size = builder->buf[0].size; + + // Save model + tfl_ctx->models[*g].model_pointer = (char *)wasm_runtime_malloc(size); + if (tfl_ctx->models[*g].model_pointer == NULL) { + NN_ERR_PRINTF("Error when allocating memory for model."); + return missing_memory; + } + + bh_memcpy_s(tfl_ctx->models[*g].model_pointer, size, builder->buf[0].buf, + size); + + // Save model flatbuffer + tfl_ctx->models[*g].model = + std::move(tflite::FlatBufferModel::BuildFromBuffer( + tfl_ctx->models[*g].model_pointer, size, NULL)); + + if (tfl_ctx->models[*g].model == NULL) { + NN_ERR_PRINTF("Loading model error."); + wasm_runtime_free(tfl_ctx->models[*g].model_pointer); + tfl_ctx->models[*g].model_pointer = NULL; + return missing_memory; + } + + // Save target + tfl_ctx->models[*g].target = target; + return success; +} + +error +tensorflowlite_init_execution_context(void *tflite_ctx, graph g, + graph_execution_context *ctx) +{ + TFLiteContext *tfl_ctx = (TFLiteContext *)tflite_ctx; + + error res; + if (success != (res = is_valid_graph(tfl_ctx, g))) + return res; + + if (success != (res = initialize_graph_ctx(tfl_ctx, g, ctx))) + return res; + + // Build the interpreter with the InterpreterBuilder. + tflite::ops::builtin::BuiltinOpResolver resolver; + tflite::InterpreterBuilder tflite_builder(*tfl_ctx->models[g].model, + resolver); + tflite_builder(&tfl_ctx->interpreters[*ctx].interpreter); + if (tfl_ctx->interpreters[*ctx].interpreter == NULL) { + NN_ERR_PRINTF("Error when generating the interpreter."); + return missing_memory; + } + + bool use_default = false; + switch (tfl_ctx->models[g].target) { + case gpu: + { +#if defined(WASI_NN_ENABLE_GPU) + NN_WARN_PRINTF("GPU enabled."); + // https://www.tensorflow.org/lite/performance/gpu + auto options = TfLiteGpuDelegateOptionsV2Default(); + options.inference_preference = + TFLITE_GPU_INFERENCE_PREFERENCE_SUSTAINED_SPEED; + options.inference_priority1 = + TFLITE_GPU_INFERENCE_PRIORITY_MIN_LATENCY; + auto *delegate = TfLiteGpuDelegateV2Create(&options); + if (tfl_ctx->interpreters[*ctx] + .interpreter->ModifyGraphWithDelegate(delegate) + != kTfLiteOk) { + NN_ERR_PRINTF("Error when enabling GPU delegate."); + use_default = true; + } +#else + NN_WARN_PRINTF("GPU not enabled."); + use_default = true; +#endif + break; + } + default: + use_default = true; + } + if (use_default) + NN_WARN_PRINTF("Default encoding is CPU."); + + tfl_ctx->interpreters[*ctx].interpreter->AllocateTensors(); + return success; +} + +error +tensorflowlite_set_input(void *tflite_ctx, graph_execution_context ctx, + uint32_t index, tensor *input_tensor) +{ + TFLiteContext *tfl_ctx = (TFLiteContext *)tflite_ctx; + + error res; + if (success != (res = is_valid_graph_execution_context(tfl_ctx, ctx))) + return res; + + uint32_t num_tensors = + tfl_ctx->interpreters[ctx].interpreter->inputs().size(); + NN_DBG_PRINTF("Number of tensors (%d)", num_tensors); + if (index + 1 > num_tensors) { + return runtime_error; + } + + auto tensor = tfl_ctx->interpreters[ctx].interpreter->input_tensor(index); + if (tensor == NULL) { + NN_ERR_PRINTF("Missing memory"); + return missing_memory; + } + + uint32_t model_tensor_size = 1; + for (int i = 0; i < tensor->dims->size; ++i) + model_tensor_size *= (uint32_t)tensor->dims->data[i]; + + uint32_t input_tensor_size = 1; + for (uint32_t i = 0; i < input_tensor->dimensions->size; i++) + input_tensor_size *= (uint32_t)input_tensor->dimensions->buf[i]; + + if (model_tensor_size != input_tensor_size) { + NN_ERR_PRINTF("Input tensor shape from the model is different than the " + "one provided"); + return invalid_argument; + } + + auto *input = + tfl_ctx->interpreters[ctx].interpreter->typed_input_tensor( + index); + if (input == NULL) + return missing_memory; + + bh_memcpy_s(input, model_tensor_size * sizeof(float), input_tensor->data, + model_tensor_size * sizeof(float)); + return success; +} + +error +tensorflowlite_compute(void *tflite_ctx, graph_execution_context ctx) +{ + TFLiteContext *tfl_ctx = (TFLiteContext *)tflite_ctx; + + error res; + if (success != (res = is_valid_graph_execution_context(tfl_ctx, ctx))) + return res; + + tfl_ctx->interpreters[ctx].interpreter->Invoke(); + return success; +} + +error +tensorflowlite_get_output(void *tflite_ctx, graph_execution_context ctx, + uint32_t index, tensor_data output_tensor, + uint32_t *output_tensor_size) +{ + TFLiteContext *tfl_ctx = (TFLiteContext *)tflite_ctx; + + error res; + if (success != (res = is_valid_graph_execution_context(tfl_ctx, ctx))) + return res; + + uint32_t num_output_tensors = + tfl_ctx->interpreters[ctx].interpreter->outputs().size(); + NN_DBG_PRINTF("Number of tensors (%d)", num_output_tensors); + + if (index + 1 > num_output_tensors) { + return runtime_error; + } + + auto tensor = tfl_ctx->interpreters[ctx].interpreter->output_tensor(index); + if (tensor == NULL) { + NN_ERR_PRINTF("Missing memory"); + return missing_memory; + } + + uint32_t model_tensor_size = 1; + for (int i = 0; i < (int)tensor->dims->size; ++i) + model_tensor_size *= (uint32_t)tensor->dims->data[i]; + + if (*output_tensor_size < model_tensor_size) { + NN_ERR_PRINTF("Insufficient memory to copy tensor %d", index); + return missing_memory; + } + + float *tensor_f = + tfl_ctx->interpreters[ctx].interpreter->typed_output_tensor( + index); + for (uint32_t i = 0; i < model_tensor_size; ++i) + NN_DBG_PRINTF("output: %f", tensor_f[i]); + + *output_tensor_size = model_tensor_size; + bh_memcpy_s(output_tensor, model_tensor_size * sizeof(float), tensor_f, + model_tensor_size * sizeof(float)); + return success; +} + +void +tensorflowlite_initialize(void **tflite_ctx) +{ + TFLiteContext *tfl_ctx = new TFLiteContext(); + if (tfl_ctx == NULL) { + NN_ERR_PRINTF("Error when allocating memory for tensorflowlite."); + return; + } + + NN_DBG_PRINTF("Initializing models."); + tfl_ctx->current_models = 0; + for (int i = 0; i < MAX_GRAPHS_PER_INST; ++i) { + tfl_ctx->models[i].model_pointer = NULL; + } + NN_DBG_PRINTF("Initializing interpreters."); + tfl_ctx->current_interpreters = 0; + + if (os_mutex_init(&tfl_ctx->g_lock) != 0) { + NN_ERR_PRINTF("Error while initializing the lock"); + } + + *tflite_ctx = (void *)tfl_ctx; +} + +void +tensorflowlite_destroy(void *tflite_ctx) +{ + /* + TensorFlow Lite memory is internally managed by tensorflow + + Related issues: + * https://github.com/tensorflow/tensorflow/issues/15880 + */ + TFLiteContext *tfl_ctx = (TFLiteContext *)tflite_ctx; + + NN_DBG_PRINTF("Freeing memory."); + for (int i = 0; i < MAX_GRAPHS_PER_INST; ++i) { + tfl_ctx->models[i].model.reset(); + if (tfl_ctx->models[i].model_pointer) + wasm_runtime_free(tfl_ctx->models[i].model_pointer); + tfl_ctx->models[i].model_pointer = NULL; + } + for (int i = 0; i < MAX_GRAPH_EXEC_CONTEXTS_PER_INST; ++i) { + tfl_ctx->interpreters[i].interpreter.reset(); + } + os_mutex_destroy(&tfl_ctx->g_lock); + delete tfl_ctx; + NN_DBG_PRINTF("Memory free'd."); +} diff --git a/core/iwasm/libraries/wasi-nn/src/wasi_nn_tensorflowlite.hpp b/core/iwasm/libraries/wasi-nn/src/wasi_nn_tensorflowlite.hpp new file mode 100644 index 000000000..9605420dd --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/src/wasi_nn_tensorflowlite.hpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef WASI_NN_TENSORFLOWLITE_HPP +#define WASI_NN_TENSORFLOWLITE_HPP + +#include "wasi_nn.h" + +#ifdef __cplusplus +extern "C" { +#endif + +error +tensorflowlite_load(void *tflite_ctx, graph_builder_array *builder, + graph_encoding encoding, execution_target target, graph *g); + +error +tensorflowlite_init_execution_context(void *tflite_ctx, graph g, + graph_execution_context *ctx); + +error +tensorflowlite_set_input(void *tflite_ctx, graph_execution_context ctx, + uint32_t index, tensor *input_tensor); + +error +tensorflowlite_compute(void *tflite_ctx, graph_execution_context ctx); + +error +tensorflowlite_get_output(void *tflite_ctx, graph_execution_context ctx, + uint32_t index, tensor_data output_tensor, + uint32_t *output_tensor_size); + +void +tensorflowlite_initialize(void **tflite_ctx); + +void +tensorflowlite_destroy(void *tflite_ctx); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/core/iwasm/libraries/wasi-nn/test/CMakeLists.txt b/core/iwasm/libraries/wasi-nn/test/CMakeLists.txt index 7951dec45..33fad71eb 100644 --- a/core/iwasm/libraries/wasi-nn/test/CMakeLists.txt +++ b/core/iwasm/libraries/wasi-nn/test/CMakeLists.txt @@ -7,10 +7,10 @@ project (iwasm) set (CMAKE_VERBOSE_MAKEFILE OFF) # Reset default linker flags -set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") -set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") set (CMAKE_C_STANDARD 99) set (CMAKE_CXX_STANDARD 14) +set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") if (NOT DEFINED WAMR_BUILD_PLATFORM) set (WAMR_BUILD_PLATFORM "linux") @@ -110,11 +110,6 @@ if (WAMR_BUILD_DEBUG_INTERP EQUAL 1) set (WAMR_BUILD_SIMD 0) endif () -if (COLLECT_CODE_COVERAGE EQUAL 1) - set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage") - set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") -endif () - set (WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../../..) include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) diff --git a/core/iwasm/libraries/wasi-nn/test/Dockerfile b/core/iwasm/libraries/wasi-nn/test/Dockerfile deleted file mode 100644 index a69b101b1..000000000 --- a/core/iwasm/libraries/wasi-nn/test/Dockerfile +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (C) 2019 Intel Corporation. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -FROM ubuntu:22.04 - -ENV DEBIAN_FRONTEND=noninteractive - -RUN apt-get update && apt-get install -y \ - cmake build-essential git wget python3.10 python3-pip - -RUN wget -q https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-14/wasi-sdk-14.0-linux.tar.gz && \ - tar xf wasi-sdk-*-linux.tar.gz -C /opt && rm -f wasi-sdk-*-linux.tar.gz && \ - mv /opt/wasi-sdk-14.0 /opt/wasi-sdk - -WORKDIR /home/wamr - -COPY core core -COPY build-scripts build-scripts -COPY product-mini product-mini - -RUN pip3 install -r core/iwasm/libraries/wasi-nn/test/requirements.txt - -WORKDIR /home/wamr/core/iwasm/libraries/wasi-nn/test/build - -RUN cmake -DWAMR_BUILD_WASI_NN=1 .. -RUN make -j $(grep -c ^processor /proc/cpuinfo) - -WORKDIR /home/wamr/core/iwasm/libraries/wasi-nn/test - -RUN ./build.sh - -ENTRYPOINT [ "./build/iwasm", "--dir=.", "test_tensorflow.wasm" ] diff --git a/core/iwasm/libraries/wasi-nn/test/Dockerfile.compile b/core/iwasm/libraries/wasi-nn/test/Dockerfile.compile new file mode 100644 index 000000000..51a59707b --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/test/Dockerfile.compile @@ -0,0 +1,23 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +FROM ubuntu:20.04 + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y \ + cmake build-essential git wget python3.10 python3-pip + +ARG WASI_SDK_VER=19 +RUN wget -c --progress=dot:giga https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_SDK_VER}/wasi-sdk-${WASI_SDK_VER}.0-linux.tar.gz -P /opt \ + && tar xf /opt/wasi-sdk-${WASI_SDK_VER}.0-linux.tar.gz -C /opt \ + && ln -fs /opt/wasi-sdk-${WASI_SDK_VER}.0 /opt/wasi-sdk \ + && rm /opt/wasi-sdk-${WASI_SDK_VER}.0-linux.tar.gz + +WORKDIR /wasi-nn/test + +COPY core/iwasm/libraries/wasi-nn/test/requirements.txt . + +RUN pip3 install -r requirements.txt && rm requirements.txt + +ENTRYPOINT [ "bash", "./build.sh" ] diff --git a/core/iwasm/libraries/wasi-nn/test/Dockerfile.cpu b/core/iwasm/libraries/wasi-nn/test/Dockerfile.cpu new file mode 100644 index 000000000..532a53365 --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/test/Dockerfile.cpu @@ -0,0 +1,27 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +FROM ubuntu:20.04 AS base + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y \ + cmake build-essential git + +WORKDIR /home/wamr + +COPY . . + +WORKDIR /home/wamr/core/iwasm/libraries/wasi-nn/test/build + +RUN cmake \ + -DWAMR_BUILD_WASI_NN=1 \ + .. + +RUN make -j $(grep -c ^processor /proc/cpuinfo) + +FROM ubuntu:22.04 + +COPY --from=base /home/wamr/core/iwasm/libraries/wasi-nn/test/build/iwasm /run/iwasm + +ENTRYPOINT [ "/run/iwasm" ] diff --git a/core/iwasm/libraries/wasi-nn/test/Dockerfile.nvidia-gpu b/core/iwasm/libraries/wasi-nn/test/Dockerfile.nvidia-gpu new file mode 100644 index 000000000..44963bd63 --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/test/Dockerfile.nvidia-gpu @@ -0,0 +1,40 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +FROM ubuntu:20.04 AS base + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y \ + cmake build-essential git + +WORKDIR /home/wamr + +COPY . . + +WORKDIR /home/wamr/core/iwasm/libraries/wasi-nn/test/build + +RUN cmake \ + -DWAMR_BUILD_WASI_NN=1 \ + -DWASI_NN_ENABLE_GPU=1 \ + .. + +RUN make -j $(grep -c ^processor /proc/cpuinfo) + +FROM nvidia/cuda:11.3.0-runtime-ubuntu20.04 + +RUN apt-get update && apt-get install -y --no-install-recommends \ + ocl-icd-libopencl1 \ + ocl-icd-opencl-dev \ + clinfo && \ + rm -rf /var/lib/apt/lists/* + +RUN mkdir -p /etc/OpenCL/vendors && \ + echo "libnvidia-opencl.so.1" > /etc/OpenCL/vendors/nvidia.icd + +ENV NVIDIA_VISIBLE_DEVICES=all +ENV NVIDIA_DRIVER_CAPABILITIES=compute,utility + +COPY --from=base /home/wamr/core/iwasm/libraries/wasi-nn/test/build/iwasm /run/iwasm + +ENTRYPOINT [ "/run/iwasm" ] diff --git a/core/iwasm/libraries/wasi-nn/test/build.sh b/core/iwasm/libraries/wasi-nn/test/build.sh index 4dc8d0151..33879eaf7 100755 --- a/core/iwasm/libraries/wasi-nn/test/build.sh +++ b/core/iwasm/libraries/wasi-nn/test/build.sh @@ -7,8 +7,9 @@ -Wl,--allow-undefined \ -Wl,--strip-all,--no-entry \ --sysroot=/opt/wasi-sdk/share/wasi-sysroot \ - -I/home/wamr/core/iwasm/libraries/wasi-nn \ - -o test_tensorflow.wasm test_tensorflow.c + -I.. -I../src/utils \ + -o test_tensorflow.wasm \ + test_tensorflow.c utils.c # TFLite models to use in the tests diff --git a/core/iwasm/libraries/wasi-nn/test/test_tensorflow.c b/core/iwasm/libraries/wasi-nn/test/test_tensorflow.c old mode 100755 new mode 100644 index 0e5e6a986..2fa516538 --- a/core/iwasm/libraries/wasi-nn/test/test_tensorflow.c +++ b/core/iwasm/libraries/wasi-nn/test/test_tensorflow.c @@ -5,194 +5,22 @@ #include #include -#include -#include -#include #include -#include "wasi_nn.h" +#include +#include -#include -#include - -#define MAX_MODEL_SIZE 85000000 -#define MAX_OUTPUT_TENSOR_SIZE 200 -#define INPUT_TENSOR_DIMS 4 -#define EPSILON 1e-8 - -typedef struct { - float *input_tensor; - uint32_t *dim; - uint32_t elements; -} input_info; - -// WASI-NN wrappers - -error -wasm_load(char *model_name, graph *graph) -{ - FILE *pFile = fopen(model_name, "r"); - if (pFile == NULL) - return invalid_argument; - - uint8_t *buffer; - size_t result; - - // allocate memory to contain the whole file: - buffer = (uint8_t *)malloc(sizeof(uint8_t) * MAX_MODEL_SIZE); - if (buffer == NULL) { - fclose(pFile); - return missing_memory; - } - - result = fread(buffer, 1, MAX_MODEL_SIZE, pFile); - if (result <= 0) { - fclose(pFile); - free(buffer); - return missing_memory; - } - - graph_builder_array arr; - - arr.size = 1; - arr.buf = (graph_builder *)malloc(sizeof(graph_builder)); - if (arr.buf == NULL) { - fclose(pFile); - free(buffer); - return missing_memory; - } - - arr.buf[0].size = result; - arr.buf[0].buf = buffer; - - error res = load(&arr, tensorflow, cpu, graph); - - fclose(pFile); - free(buffer); - free(arr.buf); - return res; -} - -error -wasm_init_execution_context(graph graph, graph_execution_context *ctx) -{ - return init_execution_context(graph, ctx); -} - -error -wasm_input(graph_execution_context ctx, float *input_tensor, uint32_t *dim) -{ - tensor_dimensions dims; - dims.size = INPUT_TENSOR_DIMS; - dims.buf = (uint32_t *)malloc(dims.size * sizeof(uint32_t)); - if (dims.buf == NULL) - return missing_memory; - - tensor tensor; - tensor.dimensions = &dims; - for (int i = 0; i < tensor.dimensions->size; ++i) - tensor.dimensions->buf[i] = dim[i]; - tensor.type = fp32; - tensor.data = (uint8_t *)input_tensor; - error err = set_input(ctx, 0, &tensor); - - free(dims.buf); - return err; -} - -error -wasm_compute(graph_execution_context ctx) -{ - return compute(ctx); -} - -error -wasm_get_output(graph_execution_context ctx, uint32_t index, float *out_tensor, - uint32_t *out_size) -{ - return get_output(ctx, index, (uint8_t *)out_tensor, out_size); -} - -// Inference - -float * -run_inference(float *input, uint32_t *input_size, uint32_t *output_size, - char *model_name, uint32_t num_output_tensors) -{ - graph graph; - if (wasm_load(model_name, &graph) != success) { - fprintf(stderr, "Error when loading model."); - exit(1); - } - - graph_execution_context ctx; - if (wasm_init_execution_context(graph, &ctx) != success) { - fprintf(stderr, "Error when initialixing execution context."); - exit(1); - } - - if (wasm_input(ctx, input, input_size) != success) { - fprintf(stderr, "Error when setting input tensor."); - exit(1); - } - - if (wasm_compute(ctx) != success) { - fprintf(stderr, "Error when running inference."); - exit(1); - } - - float *out_tensor = (float *)malloc(sizeof(float) * MAX_OUTPUT_TENSOR_SIZE); - if (out_tensor == NULL) { - fprintf(stderr, "Error when allocating memory for output tensor."); - exit(1); - } - - uint32_t offset = 0; - for (int i = 0; i < num_output_tensors; ++i) { - *output_size = MAX_OUTPUT_TENSOR_SIZE - *output_size; - if (wasm_get_output(ctx, i, &out_tensor[offset], output_size) - != success) { - fprintf(stderr, "Error when getting input ."); - exit(1); - } - - offset += *output_size; - } - *output_size = offset; - return out_tensor; -} - -// UTILS - -input_info -create_input(int *dims) -{ - input_info input = { .dim = NULL, .input_tensor = NULL, .elements = 1 }; - - input.dim = malloc(INPUT_TENSOR_DIMS * sizeof(uint32_t)); - if (input.dim) - for (int i = 0; i < INPUT_TENSOR_DIMS; ++i) { - input.dim[i] = dims[i]; - input.elements *= dims[i]; - } - - input.input_tensor = malloc(input.elements * sizeof(float)); - for (int i = 0; i < input.elements; ++i) - input.input_tensor[i] = i; - - return input; -} - -// TESTS +#include "utils.h" +#include "logger.h" void -test_sum() +test_sum(execution_target target) { int dims[] = { 1, 5, 5, 1 }; input_info input = create_input(dims); uint32_t output_size = 0; - float *output = run_inference(input.input_tensor, input.dim, &output_size, - "models/sum.tflite", 1); + float *output = run_inference(target, input.input_tensor, input.dim, + &output_size, "/assets/models/sum.tflite", 1); assert(output_size == 1); assert(fabs(output[0] - 300.0) < EPSILON); @@ -203,18 +31,18 @@ test_sum() } void -test_max() +test_max(execution_target target) { int dims[] = { 1, 5, 5, 1 }; input_info input = create_input(dims); uint32_t output_size = 0; - float *output = run_inference(input.input_tensor, input.dim, &output_size, - "models/max.tflite", 1); + float *output = run_inference(target, input.input_tensor, input.dim, + &output_size, "/assets/models/max.tflite", 1); assert(output_size == 1); assert(fabs(output[0] - 24.0) < EPSILON); - printf("Result: max is %f\n", output[0]); + NN_INFO_PRINTF("Result: max is %f", output[0]); free(input.dim); free(input.input_tensor); @@ -222,18 +50,19 @@ test_max() } void -test_average() +test_average(execution_target target) { int dims[] = { 1, 5, 5, 1 }; input_info input = create_input(dims); uint32_t output_size = 0; - float *output = run_inference(input.input_tensor, input.dim, &output_size, - "models/average.tflite", 1); + float *output = + run_inference(target, input.input_tensor, input.dim, &output_size, + "/assets/models/average.tflite", 1); assert(output_size == 1); assert(fabs(output[0] - 12.0) < EPSILON); - printf("Result: average is %f\n", output[0]); + NN_INFO_PRINTF("Result: average is %f", output[0]); free(input.dim); free(input.input_tensor); @@ -241,14 +70,15 @@ test_average() } void -test_mult_dimensions() +test_mult_dimensions(execution_target target) { int dims[] = { 1, 3, 3, 1 }; input_info input = create_input(dims); uint32_t output_size = 0; - float *output = run_inference(input.input_tensor, input.dim, &output_size, - "models/mult_dim.tflite", 1); + float *output = + run_inference(target, input.input_tensor, input.dim, &output_size, + "/assets/models/mult_dim.tflite", 1); assert(output_size == 9); for (int i = 0; i < 9; i++) @@ -260,14 +90,15 @@ test_mult_dimensions() } void -test_mult_outputs() +test_mult_outputs(execution_target target) { int dims[] = { 1, 4, 4, 1 }; input_info input = create_input(dims); uint32_t output_size = 0; - float *output = run_inference(input.input_tensor, input.dim, &output_size, - "models/mult_out.tflite", 2); + float *output = + run_inference(target, input.input_tensor, input.dim, &output_size, + "/assets/models/mult_out.tflite", 2); assert(output_size == 8); // first tensor check @@ -285,17 +116,31 @@ test_mult_outputs() int main() { - printf("################### Testing sum...\n"); - test_sum(); - printf("################### Testing max...\n"); - test_max(); - printf("################### Testing average...\n"); - test_average(); - printf("################### Testing multiple dimensions...\n"); - test_mult_dimensions(); - printf("################### Testing multiple outputs...\n"); - test_mult_outputs(); + char *env = getenv("TARGET"); + if (env == NULL) { + NN_INFO_PRINTF("Usage:\n--env=\"TARGET=[cpu|gpu]\""); + return 1; + } + execution_target target; + if (strcmp(env, "cpu") == 0) + target = cpu; + else if (strcmp(env, "gpu") == 0) + target = gpu; + else { + NN_ERR_PRINTF("Wrong target!"); + return 1; + } + NN_INFO_PRINTF("################### Testing sum..."); + test_sum(target); + NN_INFO_PRINTF("################### Testing max..."); + test_max(target); + NN_INFO_PRINTF("################### Testing average..."); + test_average(target); + NN_INFO_PRINTF("################### Testing multiple dimensions..."); + test_mult_dimensions(target); + NN_INFO_PRINTF("################### Testing multiple outputs..."); + test_mult_outputs(target); - printf("Tests: passed!\n"); + NN_INFO_PRINTF("Tests: passed!"); return 0; } diff --git a/core/iwasm/libraries/wasi-nn/test/utils.c b/core/iwasm/libraries/wasi-nn/test/utils.c new file mode 100644 index 000000000..e0704cab4 --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/test/utils.c @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "utils.h" +#include "logger.h" + +#include +#include + +error +wasm_load(char *model_name, graph *g, execution_target target) +{ + FILE *pFile = fopen(model_name, "r"); + if (pFile == NULL) + return invalid_argument; + + uint8_t *buffer; + size_t result; + + // allocate memory to contain the whole file: + buffer = (uint8_t *)malloc(sizeof(uint8_t) * MAX_MODEL_SIZE); + if (buffer == NULL) { + fclose(pFile); + return missing_memory; + } + + result = fread(buffer, 1, MAX_MODEL_SIZE, pFile); + if (result <= 0) { + fclose(pFile); + free(buffer); + return missing_memory; + } + + graph_builder_array arr; + + arr.size = 1; + arr.buf = (graph_builder *)malloc(sizeof(graph_builder)); + if (arr.buf == NULL) { + fclose(pFile); + free(buffer); + return missing_memory; + } + + arr.buf[0].size = result; + arr.buf[0].buf = buffer; + + error res = load(&arr, tensorflowlite, target, g); + + fclose(pFile); + free(buffer); + free(arr.buf); + return res; +} + +error +wasm_init_execution_context(graph g, graph_execution_context *ctx) +{ + return init_execution_context(g, ctx); +} + +error +wasm_set_input(graph_execution_context ctx, float *input_tensor, uint32_t *dim) +{ + tensor_dimensions dims; + dims.size = INPUT_TENSOR_DIMS; + dims.buf = (uint32_t *)malloc(dims.size * sizeof(uint32_t)); + if (dims.buf == NULL) + return missing_memory; + + tensor tensor; + tensor.dimensions = &dims; + for (int i = 0; i < tensor.dimensions->size; ++i) + tensor.dimensions->buf[i] = dim[i]; + tensor.type = fp32; + tensor.data = (uint8_t *)input_tensor; + error err = set_input(ctx, 0, &tensor); + + free(dims.buf); + return err; +} + +error +wasm_compute(graph_execution_context ctx) +{ + return compute(ctx); +} + +error +wasm_get_output(graph_execution_context ctx, uint32_t index, float *out_tensor, + uint32_t *out_size) +{ + return get_output(ctx, index, (uint8_t *)out_tensor, out_size); +} + +float * +run_inference(execution_target target, float *input, uint32_t *input_size, + uint32_t *output_size, char *model_name, + uint32_t num_output_tensors) +{ + graph graph; + if (wasm_load(model_name, &graph, target) != success) { + NN_ERR_PRINTF("Error when loading model."); + exit(1); + } + + graph_execution_context ctx; + if (wasm_init_execution_context(graph, &ctx) != success) { + NN_ERR_PRINTF("Error when initialixing execution context."); + exit(1); + } + + if (wasm_set_input(ctx, input, input_size) != success) { + NN_ERR_PRINTF("Error when setting input tensor."); + exit(1); + } + + if (wasm_compute(ctx) != success) { + NN_ERR_PRINTF("Error when running inference."); + exit(1); + } + + float *out_tensor = (float *)malloc(sizeof(float) * MAX_OUTPUT_TENSOR_SIZE); + if (out_tensor == NULL) { + NN_ERR_PRINTF("Error when allocating memory for output tensor."); + exit(1); + } + + uint32_t offset = 0; + for (int i = 0; i < num_output_tensors; ++i) { + *output_size = MAX_OUTPUT_TENSOR_SIZE - *output_size; + if (wasm_get_output(ctx, i, &out_tensor[offset], output_size) + != success) { + NN_ERR_PRINTF("Error when getting output."); + exit(1); + } + + offset += *output_size; + } + *output_size = offset; + return out_tensor; +} + +input_info +create_input(int *dims) +{ + input_info input = { .dim = NULL, .input_tensor = NULL, .elements = 1 }; + + input.dim = malloc(INPUT_TENSOR_DIMS * sizeof(uint32_t)); + if (input.dim) + for (int i = 0; i < INPUT_TENSOR_DIMS; ++i) { + input.dim[i] = dims[i]; + input.elements *= dims[i]; + } + + input.input_tensor = malloc(input.elements * sizeof(float)); + for (int i = 0; i < input.elements; ++i) + input.input_tensor[i] = i; + + return input; +} diff --git a/core/iwasm/libraries/wasi-nn/test/utils.h b/core/iwasm/libraries/wasi-nn/test/utils.h new file mode 100644 index 000000000..6373be542 --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/test/utils.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef WASI_NN_UTILS +#define WASI_NN_UTILS + +#include + +#include "wasi_nn.h" + +#define MAX_MODEL_SIZE 85000000 +#define MAX_OUTPUT_TENSOR_SIZE 200 +#define INPUT_TENSOR_DIMS 4 +#define EPSILON 1e-8 + +typedef struct { + float *input_tensor; + uint32_t *dim; + uint32_t elements; +} input_info; + +/* wasi-nn wrappers */ + +error +wasm_load(char *model_name, graph *g, execution_target target); + +error +wasm_init_execution_context(graph g, graph_execution_context *ctx); + +error +wasm_set_input(graph_execution_context ctx, float *input_tensor, uint32_t *dim); + +error +wasm_compute(graph_execution_context ctx); + +error +wasm_get_output(graph_execution_context ctx, uint32_t index, float *out_tensor, + uint32_t *out_size); + +/* Utils */ + +float * +run_inference(execution_target target, float *input, uint32_t *input_size, + uint32_t *output_size, char *model_name, + uint32_t num_output_tensors); + +input_info +create_input(int *dims); + +#endif diff --git a/core/iwasm/libraries/wasi-nn/wasi_nn.cmake b/core/iwasm/libraries/wasi-nn/wasi_nn.cmake index 6d34b5efe..1bb53c086 100644 --- a/core/iwasm/libraries/wasi-nn/wasi_nn.cmake +++ b/core/iwasm/libraries/wasi-nn/wasi_nn.cmake @@ -3,8 +3,15 @@ set (WASI_NN_DIR ${CMAKE_CURRENT_LIST_DIR}) -add_definitions (-DWASM_ENABLE_WASI_NN=1) +include_directories (${WASI_NN_DIR}) +include_directories (${WASI_NN_DIR}/src) +include_directories (${WASI_NN_DIR}/src/utils) -set (LIBC_WASI_NN_SOURCE ${WASI_NN_DIR}/wasi_nn_native.c ${WASI_NN_DIR}/wasi_nn_tensorflow.cpp) +set ( + LIBC_WASI_NN_SOURCE + ${WASI_NN_DIR}/src/wasi_nn.c + ${WASI_NN_DIR}/src/wasi_nn_tensorflowlite.cpp + ${WASI_NN_DIR}/src/utils/wasi_nn_app_native.c +) set (TENSORFLOW_LIB tensorflow-lite) diff --git a/core/iwasm/libraries/wasi-nn/wasi_nn.h b/core/iwasm/libraries/wasi-nn/wasi_nn.h index 115ac928a..2bf0a192c 100644 --- a/core/iwasm/libraries/wasi-nn/wasi_nn.h +++ b/core/iwasm/libraries/wasi-nn/wasi_nn.h @@ -3,63 +3,17 @@ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ -#ifndef WASI_NN_WASM_H -#define WASI_NN_WASM_H - -#include "wasi_nn_common.h" - /** * Following definition from: - * [Aug 10th, 2022] - * https://github.com/WebAssembly/wasi-nn/blob/e5e1a6c31f424c7cd63026cd270e9746775675a0/wasi-nn.wit.md + * [Oct 25th, 2022] + * https://github.com/WebAssembly/wasi-nn/blob/0f77c48ec195748990ff67928a4b3eef5f16c2de/wasi-nn.wit.md */ -/* The graph initialization data. */ +#ifndef WASI_NN_H +#define WASI_NN_H -// This consists of an array of buffers because implementing backends may encode -// their graph IR in parts (e.g., OpenVINO stores its IR and weights -// separately). -typedef struct { - uint8_t *buf; - uint32_t size; -} graph_builder; - -typedef struct { - graph_builder *buf; - uint32_t size; -} graph_builder_array; - -/* The dimensions of a tensor. */ - -// The array length matches the tensor rank and each element in the array -// describes the size of each dimension. -typedef struct { - uint32_t *buf; - uint32_t size; -} tensor_dimensions; - -/* The tensor data. */ - -// Initially conceived as a sparse representation, each empty cell would be -// filled with zeros and the array length must match the product of all of the -// dimensions and the number of bytes in the type (e.g., a 2x2 tensor with -// 4-byte f32 elements would have a data array of length 16). Naturally, this -// representation requires some knowledge of how to lay out data in -// memory--e.g., using row-major ordering--and could perhaps be improved. -typedef uint8_t *tensor_data; - -/* A tensor. */ - -typedef struct { - // Describe the size of the tensor (e.g., 2x2x2x2 -> [2, 2, 2, 2]). To - // represent a tensor containing a single value, use `[1]` for the tensor - // dimensions. - tensor_dimensions *dimensions; - // Describe the type of element in the tensor (e.g., f32). - tensor_type type; - // Contains the tensor data. - tensor_data data; -} tensor; +#include +#include "wasi_nn_types.h" /** * @brief Load an opaque sequence of bytes to use for inference. @@ -67,25 +21,31 @@ typedef struct { * @param builder Model builder. * @param encoding Model encoding. * @param target Execution target. - * @param graph Graph. + * @param g Graph. * @return error Execution status. */ error load(graph_builder_array *builder, graph_encoding encoding, - execution_target target, graph *graph) - __attribute__((export_module("wasi_nn"))) + execution_target target, graph *g) __attribute__((import_module("wasi_nn"))); +/** + * INFERENCE + * + */ + +// Bind a `graph` to the input and output tensors for an inference. +typedef uint32_t graph_execution_context; + /** * @brief Create an execution instance of a loaded graph. * - * @param graph Graph. + * @param g Graph. * @param ctx Execution context. * @return error Execution status. */ error -init_execution_context(graph graph, graph_execution_context *ctx) - __attribute__((export_module("wasi_nn"))) +init_execution_context(graph g, graph_execution_context *ctx) __attribute__((import_module("wasi_nn"))); /** @@ -98,7 +58,6 @@ init_execution_context(graph graph, graph_execution_context *ctx) */ error set_input(graph_execution_context ctx, uint32_t index, tensor *tensor) - __attribute__((export_module("wasi_nn"))) __attribute__((import_module("wasi_nn"))); /** @@ -108,8 +67,7 @@ set_input(graph_execution_context ctx, uint32_t index, tensor *tensor) * @return error Execution status. */ error -compute(graph_execution_context ctx) __attribute__((export_module("wasi_nn"))) -__attribute__((import_module("wasi_nn"))); +compute(graph_execution_context ctx) __attribute__((import_module("wasi_nn"))); /** * @brief Extract the outputs after inference. @@ -126,7 +84,6 @@ __attribute__((import_module("wasi_nn"))); error get_output(graph_execution_context ctx, uint32_t index, tensor_data output_tensor, uint32_t *output_tensor_size) - __attribute__((export_module("wasi_nn"))) __attribute__((import_module("wasi_nn"))); #endif diff --git a/core/iwasm/libraries/wasi-nn/wasi_nn_common.h b/core/iwasm/libraries/wasi-nn/wasi_nn_common.h deleted file mode 100644 index 103185bd1..000000000 --- a/core/iwasm/libraries/wasi-nn/wasi_nn_common.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef WASI_NN_COMMON_H -#define WASI_NN_COMMON_H - -#include - -// The type of the elements in a tensor. -typedef enum { fp16 = 0, fp32, up8, ip32 } tensor_type; - -// Describes the encoding of the graph. This allows the API to be implemented by -// various backends that encode (i.e., serialize) their graph IR with different -// formats. -typedef enum { openvino = 0, onnx, tensorflow, pytorch } graph_encoding; - -// Define where the graph should be executed. -typedef enum { cpu = 0, gpu, tpu } execution_target; - -// Error codes returned by functions in this API. -typedef enum { - // No error occurred. - success = 0, - // Caller module passed an invalid argument. - invalid_argument, - // Invalid encoding. - invalid_encoding, - // Caller module is missing a memory export. - missing_memory, - // Device or resource busy. - busy, - // Runtime Error. - runtime_error, -} error; - -// An execution graph for performing inference (i.e., a model). -typedef uint32_t graph; - -// Bind a `graph` to the input and output tensors for an inference. -typedef uint32_t graph_execution_context; - -#endif diff --git a/core/iwasm/libraries/wasi-nn/wasi_nn_native.c b/core/iwasm/libraries/wasi-nn/wasi_nn_native.c deleted file mode 100644 index 333dd475c..000000000 --- a/core/iwasm/libraries/wasi-nn/wasi_nn_native.c +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include -#include -#include -#include -#include - -#include "wasi_nn_common.h" -#include "wasm_export.h" -#include "bh_platform.h" - -#include "wasi_nn.h" -#include "wasi_nn_tensorflow.hpp" -#include "logger.h" - -/* Definition of 'wasi_nn.h' structs in WASM app format (using offset) */ - -typedef struct { - uint32_t buf_offset; - uint32_t size; -} graph_builder_wasm; - -typedef struct { - uint32_t buf_offset; - uint32_t size; -} graph_builder_array_wasm; - -typedef struct { - uint32_t dimensions_offset; - tensor_type type; - uint32_t data_offset; -} tensor_wasm; - -typedef struct { - uint32_t buf_offset; - uint32_t size; -} tensor_dimensions_wasm; - -/* Global variables */ - -static uint8_t _is_initialized; -static graph_encoding _encoding; - -/* Utils */ - -static error -check_initialized() -{ - if (!_is_initialized) { - NN_ERR_PRINTF("Model not initialized."); - return invalid_argument; - } - if (_encoding != tensorflow) { - NN_ERR_PRINTF("Model encoding is not tensorflow."); - return invalid_argument; - } - return success; -} - -/* WASI-NN implementation */ - -error -wasi_nn_load(wasm_exec_env_t exec_env, graph_builder_array_wasm *builder, - graph_encoding encoding, execution_target target, graph *graph) -{ - NN_DBG_PRINTF("Running wasi_nn_load [encoding=%d, target=%d]...", encoding, - target); - - wasm_module_inst_t instance = wasm_runtime_get_module_inst(exec_env); - bh_assert(instance); - - if (!wasm_runtime_validate_native_addr(instance, builder, - sizeof(graph_builder_array_wasm))) - return invalid_argument; - - if (!wasm_runtime_validate_app_addr(instance, builder->buf_offset, - builder->size * sizeof(uint32_t))) - return invalid_argument; - - NN_DBG_PRINTF("Graph builder array contains %d elements", builder->size); - - graph_builder_wasm *gb_wasm = - (graph_builder_wasm *)wasm_runtime_addr_app_to_native( - instance, builder->buf_offset); - - graph_builder *gb_native = (graph_builder *)wasm_runtime_malloc( - builder->size * sizeof(graph_builder)); - if (gb_native == NULL) - return missing_memory; - - for (int i = 0; i < builder->size; ++i) { - if (!wasm_runtime_validate_app_addr(instance, gb_wasm[i].buf_offset, - gb_wasm[i].size - * sizeof(uint8_t))) { - wasm_runtime_free(gb_native); - return invalid_argument; - } - - gb_native[i].buf = (uint8_t *)wasm_runtime_addr_app_to_native( - instance, gb_wasm[i].buf_offset); - gb_native[i].size = gb_wasm[i].size; - - NN_DBG_PRINTF("Graph builder %d contains %d elements", i, - gb_wasm[i].size); - } - - graph_builder_array gba_native = { .buf = gb_native, - .size = builder->size }; - - if (!wasm_runtime_validate_native_addr(instance, graph, sizeof(graph))) { - wasm_runtime_free(gb_native); - return invalid_argument; - } - - switch (encoding) { - case tensorflow: - break; - default: - NN_ERR_PRINTF("Only tensorflow is supported."); - wasm_runtime_free(gb_native); - return invalid_argument; - } - - _encoding = encoding; - _is_initialized = 1; - - error res = tensorflow_load(gba_native, _encoding, target, graph); - NN_DBG_PRINTF("wasi_nn_load finished with status %d [graph=%d]", res, - *graph); - - wasm_runtime_free(gb_native); - return res; -} - -error -wasi_nn_init_execution_context(wasm_exec_env_t exec_env, graph graph, - graph_execution_context *ctx) -{ - NN_DBG_PRINTF("Running wasi_nn_init_execution_context [graph=%d]...", - graph); - error res; - if (success != (res = check_initialized())) - return res; - res = tensorflow_init_execution_context(graph); - *ctx = graph; - NN_DBG_PRINTF( - "wasi_nn_init_execution_context finished with status %d [ctx=%d]", res, - *ctx); - return res; -} - -error -wasi_nn_set_input(wasm_exec_env_t exec_env, graph_execution_context ctx, - uint32_t index, tensor_wasm *input_tensor) -{ - NN_DBG_PRINTF("Running wasi_nn_set_input [ctx=%d, index=%d]...", ctx, - index); - - error res; - if (success != (res = check_initialized())) - return res; - - wasm_module_inst_t instance = wasm_runtime_get_module_inst(exec_env); - bh_assert(instance); - - if (!wasm_runtime_validate_native_addr(instance, input_tensor, - sizeof(tensor_wasm))) - return invalid_argument; - - if (!wasm_runtime_validate_app_addr( - instance, input_tensor->dimensions_offset, sizeof(uint32_t))) - return invalid_argument; - - tensor_dimensions_wasm *dimensions_w = - (tensor_dimensions_wasm *)wasm_runtime_addr_app_to_native( - instance, input_tensor->dimensions_offset); - - if (!wasm_runtime_validate_app_addr(instance, dimensions_w->buf_offset, - dimensions_w->size * sizeof(uint32_t))) - return invalid_argument; - - tensor_dimensions dimensions = { - .buf = (uint32_t *)wasm_runtime_addr_app_to_native( - instance, dimensions_w->buf_offset), - .size = dimensions_w->size - }; - - NN_DBG_PRINTF("Number of dimensions: %d", dimensions.size); - int total_elements = 1; - for (int i = 0; i < dimensions.size; ++i) { - NN_DBG_PRINTF("Dimension %d: %d", i, dimensions.buf[i]); - total_elements *= dimensions.buf[i]; - } - NN_DBG_PRINTF("Tensor type: %d", input_tensor->type); - - if (!wasm_runtime_validate_app_addr(instance, input_tensor->data_offset, - total_elements)) - return invalid_argument; - - tensor tensor = { .type = input_tensor->type, - .dimensions = &dimensions, - .data = (uint8_t *)wasm_runtime_addr_app_to_native( - instance, input_tensor->data_offset) }; - - res = tensorflow_set_input(ctx, index, &tensor); - NN_DBG_PRINTF("wasi_nn_set_input finished with status %d", res); - return res; -} - -error -wasi_nn_compute(wasm_exec_env_t exec_env, graph_execution_context ctx) -{ - NN_DBG_PRINTF("Running wasi_nn_compute [ctx=%d]...", ctx); - error res; - if (success != (res = check_initialized())) - return res; - - res = tensorflow_compute(ctx); - NN_DBG_PRINTF("wasi_nn_compute finished with status %d", res); - return res; -} - -error -wasi_nn_get_output(wasm_exec_env_t exec_env, graph_execution_context ctx, - uint32_t index, tensor_data output_tensor, - uint32_t *output_tensor_size) -{ - NN_DBG_PRINTF("Running wasi_nn_get_output [ctx=%d, index=%d]...", ctx, - index); - error res; - if (success != (res = check_initialized())) - return res; - - res = tensorflow_get_output(ctx, index, output_tensor, output_tensor_size); - NN_DBG_PRINTF("wasi_nn_get_output finished with status %d [data_size=%d]", - res, *output_tensor_size); - return res; -} - -/* Register WASI-NN in WAMR */ - -/* clang-format off */ -#define REG_NATIVE_FUNC(func_name, signature) \ - { #func_name, wasi_nn_##func_name, signature, NULL } -/* clang-format on */ - -static NativeSymbol native_symbols_wasi_nn[] = { - REG_NATIVE_FUNC(load, "(*ii*)i"), - REG_NATIVE_FUNC(init_execution_context, "(i*)i"), - REG_NATIVE_FUNC(set_input, "(ii*)i"), - REG_NATIVE_FUNC(compute, "(i)i"), - REG_NATIVE_FUNC(get_output, "(ii**)i"), -}; - -uint32_t -get_wasi_nn_export_apis(NativeSymbol **p_libc_wasi_apis) -{ - *p_libc_wasi_apis = native_symbols_wasi_nn; - return sizeof(native_symbols_wasi_nn) / sizeof(NativeSymbol); -} diff --git a/core/iwasm/libraries/wasi-nn/wasi_nn_tensorflow.cpp b/core/iwasm/libraries/wasi-nn/wasi_nn_tensorflow.cpp deleted file mode 100644 index 597b04dc2..000000000 --- a/core/iwasm/libraries/wasi-nn/wasi_nn_tensorflow.cpp +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "wasi_nn_tensorflow.hpp" -#include "wasi_nn_common.h" -#include "bh_common.h" -#include "bh_platform.h" -#include "platform_common.h" - -#include -#include -#include -#include -#include - -/* Global variables */ - -static std::unique_ptr interpreter; -static std::unique_ptr model; - -static char *model_pointer = NULL; - -/* WASI-NN (tensorflow) implementation */ - -error -tensorflow_load(graph_builder_array builder, graph_encoding encoding, - execution_target target, graph *graph) -{ - if (model_pointer != NULL) { - wasm_runtime_free(model_pointer); - model_pointer = NULL; - } - - if (builder.size != 1) { - NN_ERR_PRINTF("Unexpected builder format."); - return invalid_argument; - } - - if (encoding != tensorflow) { - NN_ERR_PRINTF("Encoding is not tensorflow."); - return invalid_argument; - } - - if (target != cpu) { - NN_ERR_PRINTF("Only CPU target is supported."); - return invalid_argument; - } - - uint32_t size = builder.buf[0].size; - - model_pointer = (char *)wasm_runtime_malloc(size); - if (model_pointer == NULL) { - NN_ERR_PRINTF("Error when allocating memory for model."); - return missing_memory; - } - - bh_memcpy_s(model_pointer, size, builder.buf[0].buf, size); - - model = tflite::FlatBufferModel::BuildFromBuffer(model_pointer, size, NULL); - if (model == NULL) { - NN_ERR_PRINTF("Loading model error."); - wasm_runtime_free(model_pointer); - model_pointer = NULL; - return missing_memory; - } - - // Build the interpreter with the InterpreterBuilder. - tflite::ops::builtin::BuiltinOpResolver resolver; - tflite::InterpreterBuilder tflite_builder(*model, resolver); - tflite_builder(&interpreter); - if (interpreter == NULL) { - NN_ERR_PRINTF("Error when generating the interpreter."); - wasm_runtime_free(model_pointer); - model_pointer = NULL; - return missing_memory; - } - - return success; -} - -error -tensorflow_init_execution_context(graph graph) -{ - if (interpreter == NULL) { - NN_ERR_PRINTF("Non-initialized interpreter."); - return runtime_error; - } - interpreter->AllocateTensors(); - return success; -} - -error -tensorflow_set_input(graph_execution_context ctx, uint32_t index, - tensor *input_tensor) -{ - if (interpreter == NULL) { - NN_ERR_PRINTF("Non-initialized interpreter."); - return runtime_error; - } - - uint32_t num_tensors = interpreter->inputs().size(); - NN_DBG_PRINTF("Number of tensors (%d)", num_tensors); - if (index + 1 > num_tensors) { - return runtime_error; - } - - auto tensor = interpreter->input_tensor(index); - if (tensor == NULL) { - NN_ERR_PRINTF("Missing memory"); - return missing_memory; - } - - uint32_t model_tensor_size = 1; - for (int i = 0; i < (int)tensor->dims->size; ++i) - model_tensor_size *= (uint32_t)tensor->dims->data[i]; - - uint32_t input_tensor_size = 1; - for (int i = 0; i < input_tensor->dimensions->size; i++) - input_tensor_size *= (uint32_t)input_tensor->dimensions->buf[i]; - - if (model_tensor_size != input_tensor_size) { - NN_ERR_PRINTF("Input tensor shape from the model is different than the " - "one provided"); - return invalid_argument; - } - - auto *input = interpreter->typed_input_tensor(index); - if (input == NULL) - return missing_memory; - - bh_memcpy_s(input, model_tensor_size * sizeof(float), input_tensor->data, - model_tensor_size * sizeof(float)); - return success; -} - -error -tensorflow_compute(graph_execution_context ctx) -{ - if (interpreter == NULL) { - NN_ERR_PRINTF("Non-initialized interpreter."); - return runtime_error; - } - interpreter->Invoke(); - return success; -} - -error -tensorflow_get_output(graph_execution_context context, uint32_t index, - tensor_data output_tensor, uint32_t *output_tensor_size) -{ - if (interpreter == NULL) { - NN_ERR_PRINTF("Non-initialized interpreter."); - return runtime_error; - } - - uint32_t num_output_tensors = interpreter->outputs().size(); - NN_DBG_PRINTF("Number of tensors (%d)", num_output_tensors); - - if (index + 1 > num_output_tensors) { - return runtime_error; - } - - auto tensor = interpreter->output_tensor(index); - if (tensor == NULL) { - NN_ERR_PRINTF("Missing memory"); - return missing_memory; - } - - uint32_t model_tensor_size = 1; - for (int i = 0; i < (int)tensor->dims->size; ++i) - model_tensor_size *= (uint32_t)tensor->dims->data[i]; - - if (*output_tensor_size < model_tensor_size) { - NN_ERR_PRINTF("Insufficient memory to copy tensor %d", index); - return missing_memory; - } - - float *tensor_f = interpreter->typed_output_tensor(index); - for (int i = 0; i < model_tensor_size; ++i) - NN_DBG_PRINTF("output: %f", tensor_f[i]); - - *output_tensor_size = model_tensor_size; - bh_memcpy_s(output_tensor, model_tensor_size * sizeof(float), tensor_f, - model_tensor_size * sizeof(float)); - return success; -} diff --git a/core/iwasm/libraries/wasi-nn/wasi_nn_tensorflow.hpp b/core/iwasm/libraries/wasi-nn/wasi_nn_tensorflow.hpp deleted file mode 100644 index 46264c0b8..000000000 --- a/core/iwasm/libraries/wasi-nn/wasi_nn_tensorflow.hpp +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef WASI_NN_TENSORFLOW_HPP -#define WASI_NN_TENSORFLOW_HPP - -#include - -#include "wasi_nn.h" -#include "logger.h" - -#ifdef __cplusplus -extern "C" { -#endif - -error -tensorflow_load(graph_builder_array builder, graph_encoding encoding, - execution_target target, graph *graph); - -error -tensorflow_init_execution_context(graph graph); - -error -tensorflow_set_input(graph_execution_context ctx, uint32_t index, - tensor *input_tensor); - -error -tensorflow_compute(graph_execution_context ctx); - -error -tensorflow_get_output(graph_execution_context context, uint32_t index, - tensor_data output_tensor, uint32_t *output_tensor_size); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/core/iwasm/libraries/wasi-nn/wasi_nn_types.h b/core/iwasm/libraries/wasi-nn/wasi_nn_types.h new file mode 100644 index 000000000..a2cebe49e --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/wasi_nn_types.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef WASI_NN_TYPES_H +#define WASI_NN_TYPES_H + +/** + * ERRORS + * + */ + +// Error codes returned by functions in this API. +typedef enum { + // No error occurred. + success = 0, + // Caller module passed an invalid argument. + invalid_argument, + // Invalid encoding. + invalid_encoding, + // Caller module is missing a memory export. + missing_memory, + // Device or resource busy. + busy, + // Runtime Error. + runtime_error, +} error; + +/** + * TENSOR + * + */ + +// The dimensions of a tensor. +// +// The array length matches the tensor rank and each element in the array +// describes the size of each dimension. +typedef struct { + uint32_t *buf; + uint32_t size; +} tensor_dimensions; + +// The type of the elements in a tensor. +typedef enum { fp16 = 0, fp32, up8, ip32 } tensor_type; + +// The tensor data. +// +// Initially conceived as a sparse representation, each empty cell would be +// filled with zeros and the array length must match the product of all of the +// dimensions and the number of bytes in the type (e.g., a 2x2 tensor with +// 4-byte f32 elements would have a data array of length 16). Naturally, this +// representation requires some knowledge of how to lay out data in +// memory--e.g., using row-major ordering--and could perhaps be improved. +typedef uint8_t *tensor_data; + +// A tensor. +typedef struct { + // Describe the size of the tensor (e.g., 2x2x2x2 -> [2, 2, 2, 2]). To + // represent a tensor containing a single value, use `[1]` for the tensor + // dimensions. + tensor_dimensions *dimensions; + // Describe the type of element in the tensor (e.g., f32). + tensor_type type; + // Contains the tensor data. + tensor_data data; +} tensor; + +/** + * GRAPH + * + */ + +// The graph initialization data. +// +// This consists of an array of buffers because implementing backends may encode +// their graph IR in parts (e.g., OpenVINO stores its IR and weights +// separately). +typedef struct { + uint8_t *buf; + uint32_t size; +} graph_builder; + +typedef struct { + graph_builder *buf; + uint32_t size; +} graph_builder_array; + +// An execution graph for performing inference (i.e., a model). +typedef uint32_t graph; + +// Describes the encoding of the graph. This allows the API to be implemented by +// various backends that encode (i.e., serialize) their graph IR with different +// formats. +typedef enum { + openvino = 0, + onnx, + tensorflow, + pytorch, + tensorflowlite +} graph_encoding; + +// Define where the graph should be executed. +typedef enum execution_target { cpu = 0, gpu, tpu } execution_target; + +#endif diff --git a/core/shared/platform/common/posix/posix_socket.c b/core/shared/platform/common/posix/posix_socket.c index d9ec9d76e..e33781d7d 100644 --- a/core/shared/platform/common/posix/posix_socket.c +++ b/core/shared/platform/common/posix/posix_socket.c @@ -44,7 +44,7 @@ textual_addr_to_sockaddr(const char *textual, int port, struct sockaddr *out, } static int -sockaddr_to_bh_sockaddr(const struct sockaddr *sockaddr, socklen_t socklen, +sockaddr_to_bh_sockaddr(const struct sockaddr *sockaddr, bh_sockaddr_t *bh_sockaddr) { switch (sockaddr->sa_family) { @@ -52,8 +52,6 @@ sockaddr_to_bh_sockaddr(const struct sockaddr *sockaddr, socklen_t socklen, { struct sockaddr_in *addr = (struct sockaddr_in *)sockaddr; - assert(socklen >= sizeof(struct sockaddr_in)); - bh_sockaddr->port = ntohs(addr->sin_port); bh_sockaddr->addr_bufer.ipv4 = ntohl(addr->sin_addr.s_addr); bh_sockaddr->is_ipv4 = true; @@ -65,8 +63,6 @@ sockaddr_to_bh_sockaddr(const struct sockaddr *sockaddr, socklen_t socklen, struct sockaddr_in6 *addr = (struct sockaddr_in6 *)sockaddr; size_t i; - assert(socklen >= sizeof(struct sockaddr_in6)); - bh_sockaddr->port = ntohs(addr->sin6_port); for (i = 0; i < sizeof(bh_sockaddr->addr_bufer.ipv6) @@ -156,17 +152,6 @@ os_socket_bind(bh_socket_t socket, const char *host, int *port) goto fail; } - if (addr.ss_family == AF_INET) { - *port = ntohs(((struct sockaddr_in *)&addr)->sin_port); - } - else { -#ifdef IPPROTO_IPV6 - *port = ntohs(((struct sockaddr_in6 *)&addr)->sin6_port); -#else - goto fail; -#endif - } - ret = fcntl(socket, F_SETFD, FD_CLOEXEC); if (ret < 0) { goto fail; @@ -187,6 +172,17 @@ os_socket_bind(bh_socket_t socket, const char *host, int *port) goto fail; } + if (addr.ss_family == AF_INET) { + *port = ntohs(((struct sockaddr_in *)&addr)->sin_port); + } + else { +#ifdef IPPROTO_IPV6 + *port = ntohs(((struct sockaddr_in6 *)&addr)->sin6_port); +#else + goto fail; +#endif + } + return BHT_OK; fail: @@ -274,8 +270,7 @@ os_socket_recv_from(bh_socket_t socket, void *buf, unsigned int len, int flags, } if (src_addr && socklen > 0) { - if (sockaddr_to_bh_sockaddr((struct sockaddr *)&sock_addr, socklen, - src_addr) + if (sockaddr_to_bh_sockaddr((struct sockaddr *)&sock_addr, src_addr) == BHT_ERROR) { return -1; } @@ -411,9 +406,8 @@ os_socket_addr_resolve(const char *host, const char *service, continue; } - ret = sockaddr_to_bh_sockaddr(res->ai_addr, - sizeof(struct sockaddr_in), - &addr_info[pos].sockaddr); + ret = + sockaddr_to_bh_sockaddr(res->ai_addr, &addr_info[pos].sockaddr); if (ret == BHT_ERROR) { freeaddrinfo(result); @@ -1014,8 +1008,7 @@ os_socket_addr_local(bh_socket_t socket, bh_sockaddr_t *sockaddr) return BHT_ERROR; } - return sockaddr_to_bh_sockaddr((struct sockaddr *)&addr_storage, addr_len, - sockaddr); + return sockaddr_to_bh_sockaddr((struct sockaddr *)&addr_storage, sockaddr); } int @@ -1031,6 +1024,5 @@ os_socket_addr_remote(bh_socket_t socket, bh_sockaddr_t *sockaddr) return BHT_ERROR; } - return sockaddr_to_bh_sockaddr((struct sockaddr *)&addr_storage, addr_len, - sockaddr); + return sockaddr_to_bh_sockaddr((struct sockaddr *)&addr_storage, sockaddr); } diff --git a/core/shared/platform/common/posix/posix_thread.c b/core/shared/platform/common/posix/posix_thread.c index 58370c203..5e814c418 100644 --- a/core/shared/platform/common/posix/posix_thread.c +++ b/core/shared/platform/common/posix/posix_thread.c @@ -61,8 +61,9 @@ os_thread_create_with_prio(korp_tid *tid, thread_start_routine_t start, pthread_attr_init(&tattr); pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE); if (pthread_attr_setstacksize(&tattr, stack_size) != 0) { - os_printf("Invalid thread stack size %u. Min stack size on Linux = %u", - stack_size, PTHREAD_STACK_MIN); + os_printf("Invalid thread stack size %u. " + "Min stack size on Linux = %u\n", + stack_size, (unsigned int)PTHREAD_STACK_MIN); pthread_attr_destroy(&tattr); return BHT_ERROR; } diff --git a/core/shared/platform/ego/platform_init.c b/core/shared/platform/ego/platform_init.c new file mode 100644 index 000000000..38a0e8049 --- /dev/null +++ b/core/shared/platform/ego/platform_init.c @@ -0,0 +1,6 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "../linux/platform_init.c" \ No newline at end of file diff --git a/core/shared/platform/ego/platform_internal.h b/core/shared/platform/ego/platform_internal.h new file mode 100644 index 000000000..1ece346be --- /dev/null +++ b/core/shared/platform/ego/platform_internal.h @@ -0,0 +1,6 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "../linux/platform_internal.h" diff --git a/core/shared/platform/ego/shared_platform.cmake b/core/shared/platform/ego/shared_platform.cmake new file mode 100644 index 000000000..9b84c5841 --- /dev/null +++ b/core/shared/platform/ego/shared_platform.cmake @@ -0,0 +1,20 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (PLATFORM_SHARED_DIR ${CMAKE_CURRENT_LIST_DIR}) + +add_definitions(-DBH_PLATFORM_EGO) + +include_directories(${PLATFORM_SHARED_DIR}) +include_directories(${PLATFORM_SHARED_DIR}/../include) + +include (${CMAKE_CURRENT_LIST_DIR}/../common/posix/platform_api_posix.cmake) + +set (PLATFORM_SHARED_SOURCE + ${PLATFORM_COMMON_POSIX_SOURCE} + ${CMAKE_CURRENT_LIST_DIR}/platform_init.c +) + +LIST (APPEND RUNTIME_LIB_HEADER_LIST + ${CMAKE_CURRENT_LIST_DIR}/platform_internal.h +) \ No newline at end of file diff --git a/core/shared/platform/esp-idf/espidf_socket.c b/core/shared/platform/esp-idf/espidf_socket.c index 37e412b25..9f441b712 100644 --- a/core/shared/platform/esp-idf/espidf_socket.c +++ b/core/shared/platform/esp-idf/espidf_socket.c @@ -77,7 +77,7 @@ os_socket_bind(bh_socket_t socket, const char *host, int *port) } socklen = sizeof(addr); - if (getsockname(socket, (void *)&addr, &socklen) == -1) { + if (getsockname(socket, (struct sockaddr *)&addr, &socklen) == -1) { goto fail; } @@ -120,7 +120,7 @@ os_socket_accept(bh_socket_t server_sock, bh_socket_t *sock, void *addr, unsigned int *addrlen) { struct sockaddr addr_tmp; - unsigned int len = sizeof(struct sockaddr); + socklen_t len = sizeof(struct sockaddr); *sock = accept(server_sock, (struct sockaddr *)&addr_tmp, &len); @@ -205,7 +205,7 @@ os_socket_addr_remote(bh_socket_t socket, bh_sockaddr_t *sockaddr) struct sockaddr_in addr; socklen_t addr_len = sizeof(addr); - if (getpeername(socket, &addr, &addr_len) == -1) { + if (getpeername(socket, (struct sockaddr *)&addr, &addr_len) == -1) { return BHT_ERROR; } @@ -219,7 +219,7 @@ os_socket_addr_local(bh_socket_t socket, bh_sockaddr_t *sockaddr) struct sockaddr_in addr; socklen_t addr_len = sizeof(addr); - if (getsockname(socket, &addr, &addr_len) == -1) { + if (getsockname(socket, (struct sockaddr *)&addr, &addr_len) == -1) { return BHT_ERROR; } diff --git a/core/shared/platform/include/platform_api_extension.h b/core/shared/platform/include/platform_api_extension.h index 42baad74f..7640e12c2 100644 --- a/core/shared/platform/include/platform_api_extension.h +++ b/core/shared/platform/include/platform_api_extension.h @@ -92,6 +92,41 @@ int os_thread_detach(korp_tid); void os_thread_exit(void *retval); +/* Try to define os_atomic_thread_fence if it isn't defined in + platform's platform_internal.h */ +#ifndef os_atomic_thread_fence + +#if !defined(__GNUC_PREREQ) && (defined(__GNUC__) || defined(__GNUG__)) \ + && !defined(__clang__) && defined(__GNUC_MINOR__) +#define __GNUC_PREREQ(maj, min) \ + ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min)) +#endif + +/* Clang's __GNUC_PREREQ macro has a different meaning than GCC one, + so we have to handle this case specially */ +#if defined(__clang__) +/* Clang provides stdatomic.h since 3.6.0 + See https://releases.llvm.org/3.6.0/tools/clang/docs/ReleaseNotes.html */ +#if __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 6) +#define BH_HAS_STD_ATOMIC +#endif +#elif defined(__GNUC_PREREQ) +/* Even though older versions of GCC support C11, atomics were + not implemented until 4.9. See + https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58016 */ +#if __GNUC_PREREQ(4, 9) +#define BH_HAS_STD_ATOMIC +#endif /* end of __GNUC_PREREQ(4, 9) */ +#endif /* end of defined(__GNUC_PREREQ) */ + +#if defined(BH_HAS_STD_ATOMIC) && !defined(__cplusplus) +#include +#define os_memory_order_release memory_order_release +#define os_atomic_thread_fence atomic_thread_fence +#endif + +#endif /* end of os_atomic_thread_fence */ + /** * Initialize current thread environment if current thread * is created by developer but not runtime diff --git a/core/shared/platform/linux-sgx/platform_internal.h b/core/shared/platform/linux-sgx/platform_internal.h index 966b8ffce..04babaf94 100644 --- a/core/shared/platform/linux-sgx/platform_internal.h +++ b/core/shared/platform/linux-sgx/platform_internal.h @@ -63,6 +63,9 @@ os_set_print_function(os_print_function_t pf); char * strcpy(char *dest, const char *src); +#define os_memory_order_release __ATOMIC_RELEASE +#define os_atomic_thread_fence __atomic_thread_fence + #ifdef __cplusplus } #endif diff --git a/core/shared/platform/linux-sgx/sgx_ipfs.c b/core/shared/platform/linux-sgx/sgx_ipfs.c index 2b0a38fff..322688980 100644 --- a/core/shared/platform/linux-sgx/sgx_ipfs.c +++ b/core/shared/platform/linux-sgx/sgx_ipfs.c @@ -16,6 +16,11 @@ #define SGX_ERROR_FILE_LOWEST_ERROR_ID SGX_ERROR_FILE_BAD_STATUS #define SGX_ERROR_FILE_HIGHEST_ERROR_ID SGX_ERROR_FILE_CLOSE_FAILED +// Internal buffer filled with zeroes and used when extending the size of +// protected files. +#define ZEROES_PADDING_LENGTH 32 * 1024 +char zeroes_padding[ZEROES_PADDING_LENGTH] = { 0 }; + // The mapping between file descriptors and IPFS file pointers. static HashMap *ipfs_file_list; @@ -78,6 +83,27 @@ ipfs_file_destroy(void *sgx_file) sgx_fclose(sgx_file); } +// Writes a given number of zeroes in file at the current offset. +// The return value is zero if successful; otherwise non-zero. +static int +ipfs_write_zeroes(void *sgx_file, size_t len) +{ + int min_count; + + while (len > 0) { + min_count = len < ZEROES_PADDING_LENGTH ? len : ZEROES_PADDING_LENGTH; + + if (sgx_fwrite(zeroes_padding, 1, min_count, sgx_file) == 0) { + errno = convert_sgx_errno(sgx_ferror(sgx_file)); + return -1; + } + + len -= min_count; + } + + return 0; +} + int ipfs_init() { @@ -104,7 +130,7 @@ ipfs_posix_fallocate(int fd, off_t offset, size_t len) // The wrapper for fseek takes care of extending the file if sought beyond // the end - if (ipfs_lseek(fd, offset + len, SEEK_CUR) == -1) { + if (ipfs_lseek(fd, offset + len, SEEK_SET) == -1) { return errno; } @@ -354,7 +380,7 @@ ipfs_fflush(int fd) off_t ipfs_lseek(int fd, off_t offset, int nwhence) { - off_t new_offset; + off_t cursor_current_location; void *sgx_file = fd2file(fd); if (!sgx_file) { errno = EBADF; @@ -364,20 +390,20 @@ ipfs_lseek(int fd, off_t offset, int nwhence) // Optimization: if the offset is 0 and the whence is SEEK_CUR, // this is equivalent of a call to ftell. if (offset == 0 && nwhence == SEEK_CUR) { - int64_t ftell_result = (off_t)sgx_ftell(sgx_file); + cursor_current_location = (off_t)sgx_ftell(sgx_file); - if (ftell_result == -1) { + if (cursor_current_location == -1) { errno = convert_sgx_errno(sgx_ferror(sgx_file)); return -1; } - return ftell_result; + return cursor_current_location; } int fseek_result = sgx_fseek(sgx_file, offset, nwhence); if (fseek_result == 0) { - new_offset = (__wasi_filesize_t)sgx_ftell(sgx_file); + off_t new_offset = (off_t)sgx_ftell(sgx_file); if (new_offset == -1) { errno = convert_sgx_errno(sgx_ferror(sgx_file)); @@ -405,17 +431,39 @@ ipfs_lseek(int fd, off_t offset, int nwhence) // manually. // Assume the error is raised because the cursor is moved beyond the end - // of the file. Try to move the cursor at the end of the file. + // of the file. + + // If the whence is the current cursor location, retrieve it + if (nwhence == SEEK_CUR) { + cursor_current_location = (off_t)sgx_ftell(sgx_file); + } + + // Move the cursor at the end of the file if (sgx_fseek(sgx_file, 0, SEEK_END) == -1) { errno = convert_sgx_errno(sgx_ferror(sgx_file)); return -1; } + // Compute the number of zeroes to append. + int64_t number_of_zeroes; + switch (nwhence) { + case SEEK_SET: + number_of_zeroes = offset - sgx_ftell(sgx_file); + break; + case SEEK_END: + number_of_zeroes = offset; + break; + case SEEK_CUR: + number_of_zeroes = + cursor_current_location + offset - sgx_ftell(sgx_file); + break; + default: + errno = EINVAL; + return -1; + } + // Write the missing zeroes - char zero = 0; - int64_t number_of_zeroes = offset - sgx_ftell(sgx_file); - if (sgx_fwrite(&zero, 1, number_of_zeroes, sgx_file) == 0) { - errno = convert_sgx_errno(sgx_ferror(sgx_file)); + if (ipfs_write_zeroes(sgx_file, number_of_zeroes) != 0) { return -1; } @@ -468,9 +516,7 @@ ipfs_ftruncate(int fd, off_t len) // Increasing the size is equal to writing from the end of the file // with null bytes. - char null_byte = 0; - if (sgx_fwrite(&null_byte, 1, len - file_size, sgx_file) == 0) { - errno = convert_sgx_errno(sgx_ferror(sgx_file)); + if (ipfs_write_zeroes(sgx_file, len - file_size) != 0) { return -1; } diff --git a/core/shared/platform/nuttx/nuttx_platform.c b/core/shared/platform/nuttx/nuttx_platform.c index 28188420e..9cb123e01 100644 --- a/core/shared/platform/nuttx/nuttx_platform.c +++ b/core/shared/platform/nuttx/nuttx_platform.c @@ -145,9 +145,115 @@ utimensat(int fd, const char *path, const struct timespec ts[2], int flag) #endif /* !defined(AT_FDCWD) */ -DIR * -fdopendir(int fd) +#ifndef CONFIG_NET + +#include + +int +accept(int sockfd, FAR struct sockaddr *addr, FAR socklen_t *addrlen) { - errno = ENOSYS; - return NULL; + errno = ENOTSUP; + return -1; } + +int +bind(int sockfd, FAR const struct sockaddr *addr, socklen_t addrlen) +{ + errno = ENOTSUP; + return -1; +} + +int +listen(int sockfd, int backlog) +{ + errno = ENOTSUP; + return -1; +} + +int +connect(int sockfd, FAR const struct sockaddr *addr, socklen_t addrlen) +{ + errno = ENOTSUP; + return -1; +} + +ssize_t +recvfrom(int sockfd, FAR void *buf, size_t len, int flags, + FAR struct sockaddr *from, FAR socklen_t *fromlen) +{ + errno = ENOTSUP; + return -1; +} + +ssize_t +send(int sockfd, FAR const void *buf, size_t len, int flags) +{ + errno = ENOTSUP; + return -1; +} + +ssize_t +sendto(int sockfd, FAR const void *buf, size_t len, int flags, + FAR const struct sockaddr *to, socklen_t tolen) +{ + errno = ENOTSUP; + return -1; +} + +int +socket(int domain, int type, int protocol) +{ + errno = ENOTSUP; + return -1; +} + +int +shutdown(int sockfd, int how) +{ + errno = ENOTSUP; + return -1; +} + +int +getaddrinfo(FAR const char *nodename, FAR const char *servname, + FAR const struct addrinfo *hints, FAR struct addrinfo **res) +{ + errno = ENOTSUP; + return -1; +} + +void +freeaddrinfo(FAR struct addrinfo *ai) +{} + +int +setsockopt(int sockfd, int level, int option, FAR const void *value, + socklen_t value_len) +{ + errno = ENOTSUP; + return -1; +} + +int +getsockopt(int sockfd, int level, int option, FAR void *value, + FAR socklen_t *value_len) +{ + errno = ENOTSUP; + return -1; +} + +int +getpeername(int sockfd, FAR struct sockaddr *addr, FAR socklen_t *addrlen) +{ + errno = ENOTSUP; + return -1; +} + +int +getsockname(int sockfd, FAR struct sockaddr *addr, FAR socklen_t *addrlen) +{ + errno = ENOTSUP; + return -1; +} + +#endif diff --git a/core/shared/platform/nuttx/platform_internal.h b/core/shared/platform/nuttx/platform_internal.h index 74fcc599d..b5bbdacd0 100644 --- a/core/shared/platform/nuttx/platform_internal.h +++ b/core/shared/platform/nuttx/platform_internal.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include diff --git a/core/shared/platform/windows/platform_internal.h b/core/shared/platform/windows/platform_internal.h index 1bce192ee..8d5c1488b 100644 --- a/core/shared/platform/windows/platform_internal.h +++ b/core/shared/platform/windows/platform_internal.h @@ -72,6 +72,7 @@ typedef struct os_thread_wait_node *os_thread_wait_list; typedef struct korp_cond { korp_mutex wait_list_lock; os_thread_wait_list thread_wait_list; + struct os_thread_wait_node *thread_wait_list_end; } korp_cond; #define bh_socket_t SOCKET @@ -115,6 +116,20 @@ os_thread_signal_inited(); #endif /* end of BUILD_TARGET_X86_64/AMD_64 */ #endif /* end of WASM_DISABLE_HW_BOUND_CHECK */ +typedef enum os_memory_order { + os_memory_order_relaxed, + os_memory_order_consume, + os_memory_order_acquire, + os_memory_order_release, + os_memory_order_acq_rel, + os_memory_order_seq_cst, +} os_memory_order; + +void +bh_atomic_thread_fence(int mem_order); + +#define os_atomic_thread_fence bh_atomic_thread_fence + #ifdef __cplusplus } #endif diff --git a/core/shared/platform/windows/shared_platform.cmake b/core/shared/platform/windows/shared_platform.cmake index 6ab29c890..414c7b1a1 100644 --- a/core/shared/platform/windows/shared_platform.cmake +++ b/core/shared/platform/windows/shared_platform.cmake @@ -10,7 +10,8 @@ add_definitions(-DHAVE_STRUCT_TIMESPEC) include_directories(${PLATFORM_SHARED_DIR}) include_directories(${PLATFORM_SHARED_DIR}/../include) -file (GLOB_RECURSE source_all ${PLATFORM_SHARED_DIR}/*.c) +file (GLOB_RECURSE source_all ${PLATFORM_SHARED_DIR}/*.c + ${PLATFORM_SHARED_DIR}/*.cpp) set (PLATFORM_SHARED_SOURCE ${source_all}) diff --git a/core/shared/platform/windows/win_atomic.cpp b/core/shared/platform/windows/win_atomic.cpp new file mode 100644 index 000000000..80e8ef518 --- /dev/null +++ b/core/shared/platform/windows/win_atomic.cpp @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" +#include "platform_api_extension.h" + +#if WASM_ENABLE_SHARED_MEMORY != 0 + +#include + +void +bh_atomic_thread_fence(int mem_order) +{ + std::memory_order order = + (std::memory_order)(std::memory_order::memory_order_relaxed + mem_order + - os_memory_order_relaxed); + std::atomic_thread_fence(order); +} + +#endif diff --git a/core/shared/platform/windows/win_thread.c b/core/shared/platform/windows/win_thread.c index a67b15e6d..09cf0c63f 100644 --- a/core/shared/platform/windows/win_thread.c +++ b/core/shared/platform/windows/win_thread.c @@ -37,6 +37,8 @@ typedef struct os_thread_data { korp_mutex wait_lock; /* Waiting list of other threads who are joining this thread */ os_thread_wait_list thread_wait_list; + /* End node of the waiting list */ + os_thread_wait_node *thread_wait_list_end; /* Whether the thread has exited */ bool thread_exited; /* Thread return value */ @@ -174,7 +176,8 @@ os_thread_cleanup(void *retval) os_sem_signal(&head->sem); head = next; } - thread_data->thread_wait_list = NULL; + thread_data->thread_wait_list = thread_data->thread_wait_list_end = + NULL; } /* Set thread status and thread return value */ thread_data->thread_exited = true; @@ -313,14 +316,14 @@ os_thread_join(korp_tid thread, void **p_retval) } /* Thread is running */ - if (!thread_data->thread_wait_list) - thread_data->thread_wait_list = &curr_thread_data->wait_node; - else { + if (!thread_data->thread_wait_list) { /* Waiting list is empty */ + thread_data->thread_wait_list = thread_data->thread_wait_list_end = + &curr_thread_data->wait_node; + } + else { /* Waiting list isn't empty */ /* Add to end of waiting list */ - os_thread_wait_node *p = thread_data->thread_wait_list; - while (p->next) - p = p->next; - p->next = &curr_thread_data->wait_node; + thread_data->thread_wait_list_end->next = &curr_thread_data->wait_node; + thread_data->thread_wait_list_end = &curr_thread_data->wait_node; } os_mutex_unlock(&thread_data->wait_lock); @@ -545,7 +548,7 @@ os_cond_init(korp_cond *cond) if (os_mutex_init(&cond->wait_list_lock) != BHT_OK) return BHT_ERROR; - cond->thread_wait_list = NULL; + cond->thread_wait_list = cond->thread_wait_list_end = NULL; return BHT_OK; } @@ -568,14 +571,13 @@ os_cond_wait_internal(korp_cond *cond, korp_mutex *mutex, bool timed, bh_assert(cond); bh_assert(mutex); os_mutex_lock(&cond->wait_list_lock); - if (!cond->thread_wait_list) - cond->thread_wait_list = node; - else { + if (!cond->thread_wait_list) { /* Waiting list is empty */ + cond->thread_wait_list = cond->thread_wait_list_end = node; + } + else { /* Waiting list isn't empty */ /* Add to end of wait list */ - os_thread_wait_node *p = cond->thread_wait_list; - while (p->next) - p = p->next; - p->next = node; + cond->thread_wait_list_end->next = node; + cond->thread_wait_list_end = node; } os_mutex_unlock(&cond->wait_list_lock); @@ -590,14 +592,24 @@ os_cond_wait_internal(korp_cond *cond, korp_mutex *mutex, bool timed, /* Remove wait node from wait list */ os_mutex_lock(&cond->wait_list_lock); - if (cond->thread_wait_list == node) + if (cond->thread_wait_list == node) { cond->thread_wait_list = node->next; + + if (cond->thread_wait_list_end == node) { + bh_assert(node->next == NULL); + cond->thread_wait_list_end = NULL; + } + } else { /* Remove from the wait list */ os_thread_wait_node *p = cond->thread_wait_list; while (p->next != node) p = p->next; p->next = node->next; + + if (cond->thread_wait_list_end == node) { + cond->thread_wait_list_end = p; + } } os_mutex_unlock(&cond->wait_list_lock); diff --git a/doc/build_wasm_app.md b/doc/build_wasm_app.md index a10a1a4cd..40f1b89dd 100644 --- a/doc/build_wasm_app.md +++ b/doc/build_wasm_app.md @@ -3,12 +3,12 @@ Prepare WASM building environments ================================== -For C and C++, WASI-SDK version 12.0+ is the major tool supported by WAMR to build WASM applications. Also, we can use [Emscripten SDK (EMSDK)](https://github.com/emscripten-core/emsdk), but it is not recommended. And there are some other compilers such as the standard clang compiler, which might also work [here](./other_wasm_compilers.md). +For C and C++, WASI-SDK version 19.0+ is the major tool supported by WAMR to build WASM applications. Also, we can use [Emscripten SDK (EMSDK)](https://github.com/emscripten-core/emsdk), but it is not recommended. And there are some other compilers such as the standard clang compiler, which might also work [here](./other_wasm_compilers.md). To install WASI SDK, please download the [wasi-sdk release](https://github.com/CraneStation/wasi-sdk/releases) and extract the archive to default path `/opt/wasi-sdk`. The official *wasi-sdk release* doesn't fully support *latest 128-bit SIMD spec* yet. WAMR provides a script in [build-wasi-sdk](../test-tools/build-wasi-sdk/) to generate -another wasi-sdk with *llvm-13* from source code and installs it at *../test-tools/wasi-sdk*. If you plan to build WASM applications with *latest 128-bit SIMD*, please use it instead of the official release. +another wasi-sdk with *llvm-15* from source code and installs it at *../test-tools/wasi-sdk*. If you plan to build WASM applications with *latest 128-bit SIMD*, please use it instead of the official release. And [sample workloads](../samples/workload) are using the self-compiled wasi-sdk. diff --git a/doc/memory_usage.md b/doc/memory_usage.md new file mode 100644 index 000000000..ec0624c6f --- /dev/null +++ b/doc/memory_usage.md @@ -0,0 +1,134 @@ +Memory usage estimation for a module +==================================== + +This document aims to provide information useful to make a rough estimation +of necessary memory to execute a WASM module. + +Instead of trying to cover every possible configurations, +the following configuration is assumed in this document: + +* Module is built with `wasi-sdk` +* Module is loaded with `wasm_runtime_load` +* AOT is used +* WASI is used +* libc heap is used +* app heap is not used +* The pthread implementation in `wasi-libc`, which is based on `wasi-threads` + (`WASM_ENABLE_LIB_WASI_THREADS`) might be used +* The another pthread implementation (`WASM_ENABLE_LIB_PTHREAD`) is not used + +Module +------ + +The memory to store the module binary is allocated by the embedder and +passed to `wasm_runtime_load`. +While WAMR owns the buffer, WAMR might make in-place modifications to +its contents. + +Loaded module and its instances +------------------------------- + +Many of data structures for module and instances are allocated from +the global heap. (aka. `wasm_runtime_malloc`) + +AOT code section +---------------- + +Memory to load AOT machine code section. + +Because this memory needs to be executable, depending on platforms, +it's allocated from a separate allocator. +For example, `mmap` and `mprotect` are used on POSIX-like platforms. + +Linear memory +------------- + +A WASM linear memory is either shared or non-shared. + +A WASM linear memory has `min` and `max` sizes. +(They correspond to `wasm-ld`'s `--init-memory` and `--max-memory` options.) +They are in the number of WASM pages, each of which is of 65536 bytes. +The `max` is optional for non-shared memory. When omitted, it effectivily +means unlimited. + +If `OS_ENABLE_HW_BOUND_CHECK` is enabled, the memory is allocated via +`os_mmap` and `os_mem_commit`/`os_mprotect`. +Otherwise, it's allocated from the global heap. + +If the memory is shared and `OS_ENABLE_HW_BOUND_CHECK` is not enabled, +the `max` size of memory is allocated on instantiation. + +Otherwise, the `min` size of memory is allocated on instantiation. +It can later grow up to the `max` size via the `memory.grow` instruction. + +Libc heap +--------- + +The libc heap is the last (highest address) part of linear memory, +which might be dynamically grown with `memory.grow` instruction, when +necessary to serve memory allocations within the module. + +App heap +-------- + +Not used for the above mentioned configuration. + +You can safely disable the app heap creation by specifying `0` for +the `heap_size` argument of `wasm_runtime_instantiate`. +(It's automatically disabled if malloc/free are exported from the module.) + +WASM stack +---------- + +Operand stack is not used for AOT. + +However, a small amount of WASM stack is used for call frames when +certain features are enabled. +(`WASM_ENABLE_DUMP_CALL_STACK` or `WASM_ENABLE_PERF_PROFILING`) + +It's allocated from the global heap. + +You can specify its size with the `stack_size` argument of +`wasm_runtime_instantiate` and `wasm_runtime_create_exec_env`. +(1 is the minimum because 0 means the default.) + +AUX stack (aka. C shadow stack) +------------------------------- + +For the main thread, it's a part of the linear memory, +between `__data_end` and `__heap_base` symbols. +You can control the size of this stack with `wasm-ld`'s +`-z stack-size` option. + +For threads created by `pthread_create`, libc allocates the stack for +them dynamically from the libc heap. +The size of this stack is inherited from the main thread's one +unless overwritten with `pthread_attr_setstacksize` etc. + +WAMR tries to detect overflow/underflow when updating the stack pointer +global. For threads created by `pthread_create`, the detection mechanism +is disabled as of writing this. + +Native stack +------------ + +The stack of the host environment thread which runs WAMR. + +For threads created by `pthread_create`, WAMR automatically creates +host threads to run those WASM threads. The stack size of these host +threads are controlled by a build-time configuration. +(`APP_THREAD_STACK_SIZE_DEFAULT`) + +In some configurations, runtime overflow can be detected using hardware traps. +(`OS_ENABLE_HW_BOUND_CHECK`) + +In some configurations, explicit overflow detection logic can be emitted +into AOT modules themselves. (cf. `os_thread_get_stack_boundary`, +`check_stack_boundary`, `wamrc --stack-bounds-checks=1/0`) + +Memory profiling +================ + +You can collect and dump detailed information about memory usage +by actually running a module with the `WASM_ENABLE_MEMORY_PROFILING` +build-time option. diff --git a/doc/port_wamr.md b/doc/port_wamr.md index 44ac50ece..7899ff318 100644 --- a/doc/port_wamr.md +++ b/doc/port_wamr.md @@ -10,9 +10,13 @@ This document describes how to port WAMR to a new platform "**new-os**" # Step 1: Implement platform API layer ------------------------- -Firstly create the folder **`core/shared/platform/new-os`** for platform API layer implementations. In the folder you just created, you must provide the following files: +Firstly create the folder for platform API layer implementations: +* for common platforms, create a folder in **`core/shared/platform/new-os`** in WAMR repository folder, so the implementation can be upstreamed +* for platforms that are internal and its implementation shouldn't be published, it's recommended to create a folder outside of the WAMR repository folder (e.g. have separate repository for platform API layer implementation) -- `platform_internal.h`: It can be used for any platform specific definitions such as macros, data types and internal APIs. +In the folder you just created, you must provide the following files: + +- `platform_internal.h`: It can be used for any platform-specific definitions such as macros, data types and internal APIs. - `shared_platform.cmake`: the cmake file will be included by the building script. It is recommended to add a definition for your platform: @@ -20,29 +24,29 @@ Firstly create the folder **`core/shared/platform/new-os`** for platform API lay add_definitions(-DBH_PLATFORM_YOUR_NAME) ``` -Then go to implement the APIs defined in following header files for the platform abstraction layer: +Then go to implement the APIs defined in the following header files for the platform abstraction layer: -- [`platform_api_vmcore.h`](../core/shared/platform/include/platform_api_vmcore.h): mandatory for building mini-product (vmcore only). Part of APIs are needed only for Ahead of Time compilation support. +- [`platform_api_vmcore.h`](../core/shared/platform/include/platform_api_vmcore.h): mandatory for building mini-product (vmcore only). Part of the APIs is needed only for Ahead of Time compilation support. - [`platform_api_extension.h`](../core/shared/platform/include/platform_api_extension.h): mandatory for app-mgr and app-framework. Given that the app-mgr and app-framework are not required for your target platform, you won't have to implement the API defined in the `platform_api_extension.h`. **common/posix:** -There is posix based implementation of the platform API located in the `platform/common/posix` folder. You can include it if your platform support posix API. refer to platform linux implementation. +There is posix based implementation of the platform API located in the `platform/common/posix` folder. You can include it if your platform supports posix API. refer to platform linux implementation. **common/math:** -Some platforms such as ZephyrOS don't provide math functions e.g. sqrt, fabs and isnan, then you should include source files under the folder `platform/common/math`. +Some platforms such as ZephyrOS don't provide math functions e.g. sqrt, fabs and isnan, then you should include source files under the folder `platform/common/math`. # Step 2: Create the mini product for the platform ------------------------- -You can build a mini WAMR product which is only the vmcore for you platform. Normally you need to implement the main function which loads a WASM file and run it with the WASM runtime. You don't have to do this step if there is no mini-product need for your platform porting. +You can build a mini WAMR product which is only the vmcore for your platform. Normally you need to implement the main function which loads a WASM file and run it with the WASM runtime. You don't have to do this step if there is no mini-product need for your platform porting. @@ -55,9 +59,14 @@ You should set cmake variable `WAMR_BUILD_PLATFORM` to your platform name while ``` mkdir build cd build -cmake .. -DWAMR_BUILD_PLATFORM=new-os +cmake .. -DWAMR_BUILD_PLATFORM=new-os ``` +For platform implementations that are outside of the WAMR repository (e.g. internal platforms), you also need to provide `SHARED_PLATFORM_CONFIG` path: + +``` +cmake .. -DWAMR_BUILD_PLATFORM=new-os -DSHARED_PLATFORM_CONFIG=/path/to/new-os/shared_platform.cmake +``` Refer to [build_wamr.md](./build_wamr.md) for the building configurations and parameters. diff --git a/language-bindings/go/build.sh b/language-bindings/go/build.sh index 1540c6640..fe46a9a83 100755 --- a/language-bindings/go/build.sh +++ b/language-bindings/go/build.sh @@ -7,6 +7,12 @@ PLATFORM=$(uname -s | tr A-Z a-z) CUR_DIR=$PWD WAMR_DIR=$PWD/../.. WAMR_GO_DIR=$PWD/wamr +ARCH=$(uname -m) +if [ ${ARCH} = "arm64" ]; then + ARCH="aarch64" +elif [ ${ARCH} = "x86_64" ]; then + ARCH="amd64" +fi cp -a ${WAMR_DIR}/core/iwasm/include/*.h ${WAMR_GO_DIR}/packaged/include @@ -15,7 +21,7 @@ cmake ${WAMR_DIR}/product-mini/platforms/${PLATFORM} \ -DWAMR_BUILD_LIB_PTHREAD=1 -DWAMR_BUILD_DUMP_CALL_STACK=1 \ -DWAMR_BUILD_MEMORY_PROFILING=1 make -j ${nproc} -cp -a libvmlib.a ${WAMR_GO_DIR}/packaged/lib/${PLATFORM}-amd64 +cp -a libvmlib.a ${WAMR_GO_DIR}/packaged/lib/${PLATFORM}-${ARCH} cd ${WAMR_GO_DIR} go test diff --git a/language-bindings/go/go.sum b/language-bindings/go/go.sum index b380ae445..acb88a48f 100644 --- a/language-bindings/go/go.sum +++ b/language-bindings/go/go.sum @@ -5,6 +5,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/language-bindings/go/wamr/packaged/lib/darwin-aarch64/dummy.go b/language-bindings/go/wamr/packaged/lib/darwin-aarch64/dummy.go new file mode 100644 index 000000000..35f3c705f --- /dev/null +++ b/language-bindings/go/wamr/packaged/lib/darwin-aarch64/dummy.go @@ -0,0 +1,6 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +package darwin_aarch64 diff --git a/language-bindings/python/MANIFEST.in b/language-bindings/python/MANIFEST.in new file mode 100644 index 000000000..9b0c0893f --- /dev/null +++ b/language-bindings/python/MANIFEST.in @@ -0,0 +1 @@ +include src/wamr/libs/* diff --git a/language-bindings/python/README.md b/language-bindings/python/README.md index 2698a059e..9e504a9c0 100644 --- a/language-bindings/python/README.md +++ b/language-bindings/python/README.md @@ -1,31 +1,34 @@ # wamr-python +The WAMR Python package contains a set of high-level bindings for WAMR API and WASM-C-API. + ## Installation -### Installing from the source code - -Installing from local source tree is in _development mode_. The package appears to be installed but still is editable from the source tree. +To Install from local source tree in _development mode_ run the following command, ```bash -$ python -m pip install -e /path/to/wamr-root/binding/python +python -m pip install -e . ``` +In this mode the package appears to be installed but still is editable from the source tree. + ## Usage -```python -import wamr.ffi as ffi +From the same package you can use two set of APIs. + +To use the WAMR API you can import the symbols as follows, + +```py +from wamr.wamrapi.wamr import Engine, Module, Instance, ExecEnv ``` -### Preparation +In the order hand, to use the WASM-C-API, -The binding will load the shared library _libiwasm.so_ from the WAMR repo. So before running the binding, you need to build the library yourself. +```py +import wamr.wasmcapi.ffi as ffi +``` -The default compile options are good enough. +For more information: -Please be aware that `wasm_frame_xxx` and `wasm_trap_xxx` only work well when enabling `WAMR_BUILD_DUMP_CALL_STACK`. - -### Examples - -There is a [simple example](./samples/hello_procedural.py) to show how to use bindings. Actually, the python binding follows C-APIs. There it should be easy if be familiar with _programming with wasm-c-api_. - -Unit test cases under _./tests_ could be another but more complete references. +* [WAMR API](./wamr_api) +* [WASM-C-API](./wasm_c_api) diff --git a/language-bindings/python/pyproject.toml b/language-bindings/python/pyproject.toml index b0f076532..a480ac671 100644 --- a/language-bindings/python/pyproject.toml +++ b/language-bindings/python/pyproject.toml @@ -1,3 +1,3 @@ [build-system] -requires = ["setuptools>=42"] +requires = ["setuptools>=42", "ctypesgen==1.1.1"] build-backend = "setuptools.build_meta" diff --git a/language-bindings/python/setup.py b/language-bindings/python/setup.py index c7868187b..fb7993e68 100755 --- a/language-bindings/python/setup.py +++ b/language-bindings/python/setup.py @@ -8,7 +8,35 @@ # pylint: disable=missing-function-docstring # pylint: disable=missing-module-docstring +import pathlib from setuptools import setup, find_packages +from setuptools.command.develop import develop +from setuptools.command.install import install +from setuptools.command.egg_info import egg_info +from subprocess import check_call + + +def build_library(): + cur_path = pathlib.Path(__file__).parent + check_call(f"{cur_path}/utils/create_lib.sh".split()) + + +class PreDevelopCommand(develop): + def run(self): + build_library() + develop.run(self) + + +class PreInstallCommand(install): + def run(self): + build_library() + install.run(self) + + +class PreEggInfoCommand(egg_info): + def run(self): + build_library() + egg_info.run(self) with open("README.md") as f: @@ -22,9 +50,16 @@ setup( version="0.1.0", description="A WebAssembly runtime powered by WAMR", long_description=readme, + packages=find_packages(where="src"), + package_dir={"": "src"}, author="The WAMR Project Developers", author_email="hello@bytecodealliance.org", - url="https://github.com/bytecodealliance/wamr-python", + url="https://github.com/bytecodealliance/wasm-micro-runtime", license=license, - packages=["wamr"], + include_package_data=True, + cmdclass={ + 'develop': PreDevelopCommand, + 'install': PreInstallCommand, + 'egg_info': PreEggInfoCommand, + }, ) diff --git a/language-bindings/python/src/wamr/__init__.py b/language-bindings/python/src/wamr/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/language-bindings/python/src/wamr/libs/.placeholder b/language-bindings/python/src/wamr/libs/.placeholder new file mode 100644 index 000000000..e69de29bb diff --git a/language-bindings/python/src/wamr/wamrapi/__init__.py b/language-bindings/python/src/wamr/wamrapi/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/language-bindings/python/src/wamr/wamrapi/wamr.py b/language-bindings/python/src/wamr/wamrapi/wamr.py new file mode 100644 index 000000000..abbd23227 --- /dev/null +++ b/language-bindings/python/src/wamr/wamrapi/wamr.py @@ -0,0 +1,149 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +from ctypes import Array +from ctypes import c_char +from ctypes import c_uint +from ctypes import c_uint8 +from ctypes import c_void_p +from ctypes import cast +from ctypes import create_string_buffer +from ctypes import POINTER +from ctypes import pointer +from wamr.wamrapi.iwasm import String +from wamr.wamrapi.iwasm import Alloc_With_Pool +from wamr.wamrapi.iwasm import RuntimeInitArgs +from wamr.wamrapi.iwasm import wasm_exec_env_t +from wamr.wamrapi.iwasm import wasm_function_inst_t +from wamr.wamrapi.iwasm import wasm_module_inst_t +from wamr.wamrapi.iwasm import wasm_module_t +from wamr.wamrapi.iwasm import wasm_runtime_call_wasm +from wamr.wamrapi.iwasm import wasm_runtime_create_exec_env +from wamr.wamrapi.iwasm import wasm_runtime_deinstantiate +from wamr.wamrapi.iwasm import wasm_runtime_destroy +from wamr.wamrapi.iwasm import wasm_runtime_destroy_exec_env +from wamr.wamrapi.iwasm import wasm_runtime_full_init +from wamr.wamrapi.iwasm import wasm_runtime_instantiate +from wamr.wamrapi.iwasm import wasm_runtime_load +from wamr.wamrapi.iwasm import wasm_runtime_lookup_function +from wamr.wamrapi.iwasm import wasm_runtime_unload +from wamr.wamrapi.iwasm import wasm_runtime_module_malloc +from wamr.wamrapi.iwasm import wasm_runtime_module_free +from wamr.wamrapi.iwasm import wasm_runtime_register_natives +from wamr.wamrapi.iwasm import NativeSymbol + + +class Engine: + def __init__(self): + self._native_symbols = dict() + self.init_args = self._get_init_args() + wasm_runtime_full_init(pointer(self.init_args)) + + def __del__(self): + print("deleting Engine") + wasm_runtime_destroy() + + def _get_init_args(self, heap_size: int = 1024 * 512) -> RuntimeInitArgs: + init_args = RuntimeInitArgs() + init_args.mem_alloc_type = Alloc_With_Pool + init_args.mem_alloc_option.pool.heap_buf = cast( + (c_char * heap_size)(), c_void_p + ) + init_args.mem_alloc_option.pool.heap_size = heap_size + return init_args + + def register_natives(self, module_name: str, native_symbols: list[NativeSymbol]) -> None: + module_name = String.from_param(module_name) + # WAMR does not copy the symbols. We must store them. + for native in native_symbols: + self._native_symbols[str(native.symbol)] = (module_name, native) + + if not wasm_runtime_register_natives( + module_name, + cast( + (NativeSymbol * len(native_symbols))(*native_symbols), + POINTER(NativeSymbol) + ), + len(native_symbols) + ): + raise Exception("Error while registering symbols") + +class Module: + __create_key = object() + + @classmethod + def from_file(cls, engine: Engine, fp: str) -> "Module": + return Module(cls.__create_key, engine, fp) + + def __init__(self, create_key: object, engine: Engine, fp: str) -> None: + assert ( + create_key == Module.__create_key + ), "Module objects must be created using Module.from_file" + self.engine = engine + self.module, self.file_data = self._create_module(fp) + + def __del__(self): + print("deleting Module") + wasm_runtime_unload(self.module) + + def _create_module(self, fp: str) -> tuple[wasm_module_t, Array[c_uint]]: + with open(fp, "rb") as f: + data = f.read() + data = (c_uint8 * len(data))(*data) + + error_buf = create_string_buffer(128) + module = wasm_runtime_load(data, len(data), error_buf, len(error_buf)) + if not module: + raise Exception("Error while creating module") + return module, data + + +class Instance: + def __init__(self, module: Module, stack_size: int = 65536, heap_size: int = 16384): + self.module = module + self.module_inst = self._create_module_inst(module, stack_size, heap_size) + + def __del__(self): + print("deleting Instance") + wasm_runtime_deinstantiate(self.module_inst) + + def malloc(self, nbytes: int, native_handler) -> c_uint: + return wasm_runtime_module_malloc(self.module_inst, nbytes, native_handler) + + def free(self, wasm_handler) -> None: + wasm_runtime_module_free(self.module_inst, wasm_handler) + + def lookup_function(self, name: str) -> wasm_function_inst_t: + func = wasm_runtime_lookup_function(self.module_inst, name, None) + if not func: + raise Exception("Error while looking-up function") + return func + + def _create_module_inst(self, module: Module, stack_size: int, heap_size: int) -> wasm_module_inst_t: + error_buf = create_string_buffer(128) + module_inst = wasm_runtime_instantiate( + module.module, stack_size, heap_size, error_buf, len(error_buf) + ) + if not module_inst: + raise Exception("Error while creating module instance") + return module_inst + + +class ExecEnv: + def __init__(self, module_inst: Instance, stack_size: int = 65536): + self.module_inst = module_inst + self.exec_env = self._create_exec_env(module_inst, stack_size) + + def __del__(self): + print("deleting ExecEnv") + wasm_runtime_destroy_exec_env(self.exec_env) + + def call(self, func: wasm_function_inst_t, argc: int, argv: "POINTER[c_uint]"): + if not wasm_runtime_call_wasm(self.exec_env, func, argc, argv): + raise Exception("Error while calling function") + + def _create_exec_env(self, module_inst: Instance, stack_size: int) -> wasm_exec_env_t: + exec_env = wasm_runtime_create_exec_env(module_inst.module_inst, stack_size) + if not exec_env: + raise Exception("Error while creating execution environment") + return exec_env diff --git a/language-bindings/python/wamr/__init__.py b/language-bindings/python/src/wamr/wasmcapi/__init__.py similarity index 100% rename from language-bindings/python/wamr/__init__.py rename to language-bindings/python/src/wamr/wasmcapi/__init__.py diff --git a/language-bindings/python/wamr/binding.py b/language-bindings/python/src/wamr/wasmcapi/binding.py similarity index 100% rename from language-bindings/python/wamr/binding.py rename to language-bindings/python/src/wamr/wasmcapi/binding.py diff --git a/language-bindings/python/wamr/ffi.py b/language-bindings/python/src/wamr/wasmcapi/ffi.py similarity index 99% rename from language-bindings/python/wamr/ffi.py rename to language-bindings/python/src/wamr/wasmcapi/ffi.py index a29b607c1..18b6bc90c 100644 --- a/language-bindings/python/wamr/ffi.py +++ b/language-bindings/python/src/wamr/wasmcapi/ffi.py @@ -36,8 +36,8 @@ current_file = Path(__file__) if current_file.is_symlink(): current_file = Path(os.readlink(current_file)) current_dir = current_file.parent.resolve() -root_dir = current_dir.parent.parent.parent.parent.resolve() -wamr_dir = root_dir.joinpath("wasm-micro-runtime").resolve() +root_dir = current_dir.parents[4].resolve() +wamr_dir = root_dir.resolve() if not wamr_dir.exists(): raise RuntimeError(f"not found the repo of wasm-micro-runtime under {root_dir}") diff --git a/language-bindings/python/utils/create_lib.sh b/language-bindings/python/utils/create_lib.sh new file mode 100755 index 000000000..b7e10d3ee --- /dev/null +++ b/language-bindings/python/utils/create_lib.sh @@ -0,0 +1,32 @@ +#!/bin/sh + +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +CUR_DIR=$(cd $(dirname $0) && pwd -P) +ROOT_DIR=${CUR_DIR}/../../.. + +UNAME=$(uname -s|tr A-Z a-z) +WAMR_BUILD_PLATFORM=${WAMR_BUILD_PLATFORM:-${UNAME}} + +cd ${ROOT_DIR}/product-mini/platforms/${WAMR_BUILD_PLATFORM} + +mkdir -p build && cd build +cmake .. +make -j + +case ${UNAME} in +darwin) + LIBNAME=libiwasm.dylib + ;; +*) + LIBNAME=libiwasm.so + ;; +esac +cp ${LIBNAME} ${CUR_DIR}/../src/wamr/libs + +cd ${ROOT_DIR}/language-bindings/python/src/wamr/wamrapi +ctypesgen \ +${ROOT_DIR}/core/iwasm/include/wasm_export.h \ +-l ../libs/${LIBNAME} \ +-o iwasm.py diff --git a/language-bindings/python/wamr-api/README.md b/language-bindings/python/wamr-api/README.md new file mode 100644 index 000000000..5ee672e29 --- /dev/null +++ b/language-bindings/python/wamr-api/README.md @@ -0,0 +1,29 @@ +# WARM API + +## Setup + +### Pre-requisites + +Install requirements, + +``` +pip install -r requirements.txt +``` + +### Build native lib and update bindings + +The following command builds the iwasm library and generates the Python bindings, + +```sh +bash language-bindings/python/utils/create_lib.sh +``` + +This will build and copy libiwasm into the package. + +## Examples + +There is a [simple example](./samples/main.py) to show how to use bindings. + +``` +python samples/main.py +``` diff --git a/language-bindings/python/wamr-api/requirements.txt b/language-bindings/python/wamr-api/requirements.txt new file mode 100644 index 000000000..923575a44 --- /dev/null +++ b/language-bindings/python/wamr-api/requirements.txt @@ -0,0 +1 @@ +ctypesgen==1.1.1 \ No newline at end of file diff --git a/language-bindings/python/wamr-api/samples/compile.sh b/language-bindings/python/wamr-api/samples/compile.sh new file mode 100644 index 000000000..4745ef1e8 --- /dev/null +++ b/language-bindings/python/wamr-api/samples/compile.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +/opt/wasi-sdk/bin/clang \ + -O0 -z stack-size=4096 -Wl,--initial-memory=65536 \ + -Wl,--strip-all,--no-entry -nostdlib \ + -Wl,--export=sum\ + -Wl,--allow-undefined \ + -o sum.wasm sum.c diff --git a/language-bindings/python/wamr-api/samples/main.py b/language-bindings/python/wamr-api/samples/main.py new file mode 100644 index 000000000..525aab810 --- /dev/null +++ b/language-bindings/python/wamr-api/samples/main.py @@ -0,0 +1,22 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +from wamr.wamrapi.wamr import Engine, Module, Instance, ExecEnv +from ctypes import c_uint +import pathlib + +def main(): + engine = Engine() + module = Module.from_file(engine, pathlib.Path(__file__).parent / "sum.wasm") + module_inst = Instance(module) + exec_env = ExecEnv(module_inst) + + func = module_inst.lookup_function("sum") + + argv = (c_uint * 2)(*[10, 11]) + exec_env.call(func, len(argv), argv) + print(argv[0]) + + +if __name__ == "__main__": + main() diff --git a/language-bindings/python/wamr-api/samples/sum.c b/language-bindings/python/wamr-api/samples/sum.c new file mode 100644 index 000000000..586c5bc6a --- /dev/null +++ b/language-bindings/python/wamr-api/samples/sum.c @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include + +int +sum(int a, int b) +{ + return a + b; +} diff --git a/language-bindings/python/wasm-c-api/README.md b/language-bindings/python/wasm-c-api/README.md new file mode 100644 index 000000000..1684afa5c --- /dev/null +++ b/language-bindings/python/wasm-c-api/README.md @@ -0,0 +1,7 @@ +# WASM-C-API + +## Examples + +There is a [simple example](./samples/hello_procedural.py) to show how to use bindings. Actually, the python binding follows C-APIs. There it should be easy if be familiar with _programming with wasm-c-api_. + +Unit test cases under _./tests_ could be another but more complete references. diff --git a/language-bindings/python/docs/design.md b/language-bindings/python/wasm-c-api/docs/design.md similarity index 100% rename from language-bindings/python/docs/design.md rename to language-bindings/python/wasm-c-api/docs/design.md diff --git a/language-bindings/python/docs/images/python_package_life_cycle.png b/language-bindings/python/wasm-c-api/docs/images/python_package_life_cycle.png similarity index 100% rename from language-bindings/python/docs/images/python_package_life_cycle.png rename to language-bindings/python/wasm-c-api/docs/images/python_package_life_cycle.png diff --git a/language-bindings/python/docs/setup_dev_env.md b/language-bindings/python/wasm-c-api/docs/setup_dev_env.md similarity index 100% rename from language-bindings/python/docs/setup_dev_env.md rename to language-bindings/python/wasm-c-api/docs/setup_dev_env.md diff --git a/language-bindings/python/requirements.txt b/language-bindings/python/wasm-c-api/requirements.txt similarity index 100% rename from language-bindings/python/requirements.txt rename to language-bindings/python/wasm-c-api/requirements.txt diff --git a/language-bindings/python/samples/hello.wat b/language-bindings/python/wasm-c-api/samples/hello.wat similarity index 100% rename from language-bindings/python/samples/hello.wat rename to language-bindings/python/wasm-c-api/samples/hello.wat diff --git a/language-bindings/python/samples/hello_oop.py b/language-bindings/python/wasm-c-api/samples/hello_oop.py similarity index 100% rename from language-bindings/python/samples/hello_oop.py rename to language-bindings/python/wasm-c-api/samples/hello_oop.py diff --git a/language-bindings/python/samples/hello_procedural.py b/language-bindings/python/wasm-c-api/samples/hello_procedural.py similarity index 98% rename from language-bindings/python/samples/hello_procedural.py rename to language-bindings/python/wasm-c-api/samples/hello_procedural.py index ed3002d5b..5924423bd 100644 --- a/language-bindings/python/samples/hello_procedural.py +++ b/language-bindings/python/wasm-c-api/samples/hello_procedural.py @@ -5,7 +5,7 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # import ctypes -import wamr.ffi as ffi +import wamr.wasmcapi.ffi as ffi WAMS_BINARY_CONTENT = ( b"\x00asm\x01\x00\x00\x00\x01\x84\x80\x80\x80\x00\x01`\x00\x00\x02\x8a\x80" diff --git a/language-bindings/python/tests/__init__.py b/language-bindings/python/wasm-c-api/tests/__init__.py similarity index 100% rename from language-bindings/python/tests/__init__.py rename to language-bindings/python/wasm-c-api/tests/__init__.py diff --git a/language-bindings/python/tests/context.py b/language-bindings/python/wasm-c-api/tests/context.py similarity index 100% rename from language-bindings/python/tests/context.py rename to language-bindings/python/wasm-c-api/tests/context.py diff --git a/language-bindings/python/tests/test_advanced.py b/language-bindings/python/wasm-c-api/tests/test_advanced.py similarity index 99% rename from language-bindings/python/tests/test_advanced.py rename to language-bindings/python/wasm-c-api/tests/test_advanced.py index ad074f2b3..2e1c285ea 100644 --- a/language-bindings/python/tests/test_advanced.py +++ b/language-bindings/python/wasm-c-api/tests/test_advanced.py @@ -12,7 +12,7 @@ import ctypes as c import math import unittest -import wamr.ffi as ffi +import wamr.wasmcapi.ffi as ffi # It is a module likes: diff --git a/language-bindings/python/tests/test_basic.py b/language-bindings/python/wasm-c-api/tests/test_basic.py similarity index 99% rename from language-bindings/python/tests/test_basic.py rename to language-bindings/python/wasm-c-api/tests/test_basic.py index 556162bde..632ad512a 100644 --- a/language-bindings/python/tests/test_basic.py +++ b/language-bindings/python/wasm-c-api/tests/test_basic.py @@ -12,7 +12,7 @@ import ctypes as c import unittest from venv import create -from wamr.ffi import * +from wamr.wasmcapi.ffi import * # It is a module likes: # (module diff --git a/language-bindings/python/utils/bindgen.py b/language-bindings/python/wasm-c-api/utils/bindgen.py similarity index 98% rename from language-bindings/python/utils/bindgen.py rename to language-bindings/python/wasm-c-api/utils/bindgen.py index 6371ca657..a505404d5 100644 --- a/language-bindings/python/utils/bindgen.py +++ b/language-bindings/python/wasm-c-api/utils/bindgen.py @@ -21,7 +21,7 @@ import sys from pycparser import c_ast, parse_file WASM_C_API_HEADER = "core/iwasm/include/wasm_c_api.h" -BINDING_PATH = "wamr/binding.py" +BINDING_PATH = "language-bindings/python/wamr/wasmcapi/binding.py" # 4 spaces as default indent INDENT = " " @@ -314,7 +314,7 @@ class Visitor(c_ast.NodeVisitor): def preflight_check(workspace): - wamr_repo = workspace.joinpath("wasm-micro-runtime") + wamr_repo = workspace file_check_list = [ wamr_repo.exists(), wamr_repo.joinpath(WASM_C_API_HEADER).exists(), @@ -369,12 +369,12 @@ def main(): current_file = pathlib.Path(os.readlink(current_file)) current_dir = current_file.parent.resolve() - root_dir = current_dir.joinpath("..").resolve() + root_dir = current_dir.joinpath("../../../..").resolve() if not preflight_check(root_dir): return False - wamr_repo = root_dir.joinpath("wasm-micro-runtime") + wamr_repo = root_dir binding_file_path = root_dir.joinpath(BINDING_PATH) with open(binding_file_path, "wt", encoding="utf-8") as binding_file: binding_file.write(do_parse(wamr_repo)) diff --git a/product-mini/platforms/linux-sgx/CMakeLists.txt b/product-mini/platforms/linux-sgx/CMakeLists.txt index 306f634e7..e1cbe2cc7 100644 --- a/product-mini/platforms/linux-sgx/CMakeLists.txt +++ b/product-mini/platforms/linux-sgx/CMakeLists.txt @@ -89,10 +89,6 @@ if (NOT DEFINED WAMR_BUILD_SGX_IPFS) set (WAMR_BUILD_SGX_IPFS 0) endif () -if (COLLECT_CODE_COVERAGE EQUAL 1) - set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage") -endif () - set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu11 -ffunction-sections -fdata-sections \ -Wall -Wno-unused-parameter -Wno-pedantic \ diff --git a/product-mini/platforms/linux-sgx/CMakeLists_minimal.txt b/product-mini/platforms/linux-sgx/CMakeLists_minimal.txt index b79565f19..aa3de6dac 100644 --- a/product-mini/platforms/linux-sgx/CMakeLists_minimal.txt +++ b/product-mini/platforms/linux-sgx/CMakeLists_minimal.txt @@ -69,10 +69,6 @@ if (NOT DEFINED WAMR_BUILD_LIB_PTHREAD) set (WAMR_BUILD_LIB_PTHREAD 0) endif () -if (COLLECT_CODE_COVERAGE EQUAL 1) - set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage") -endif () - set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -ffunction-sections -fdata-sections \ -Wall -Wno-unused-parameter -Wno-pedantic \ diff --git a/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave.cpp b/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave.cpp index 7cb9c3f47..302743594 100644 --- a/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave.cpp +++ b/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave.cpp @@ -277,7 +277,6 @@ static void handle_cmd_unload_module(uint64 *args, uint32 argc) { EnclaveModule *enclave_module = *(EnclaveModule **)args++; - uint32 i; bh_assert(argc == 1); diff --git a/product-mini/platforms/linux/CMakeLists.txt b/product-mini/platforms/linux/CMakeLists.txt index d3285be19..cc7ff8de9 100644 --- a/product-mini/platforms/linux/CMakeLists.txt +++ b/product-mini/platforms/linux/CMakeLists.txt @@ -85,6 +85,12 @@ if (NOT DEFINED WAMR_BUILD_LIB_PTHREAD) set (WAMR_BUILD_LIB_PTHREAD 0) endif () +if (NOT DEFINED WAMR_BUILD_LIB_WASI_THREADS) + # Disable wasi threads library by default + set (WAMR_BUILD_LIB_WASI_THREADS 0) +endif() + + if (NOT DEFINED WAMR_BUILD_MINI_LOADER) # Disable wasm mini loader by default set (WAMR_BUILD_MINI_LOADER 0) @@ -111,11 +117,6 @@ if (WAMR_BUILD_DEBUG_INTERP EQUAL 1) set (WAMR_BUILD_SIMD 0) endif () -if (COLLECT_CODE_COVERAGE EQUAL 1) - set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage") - set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") -endif () - set (WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..) include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) diff --git a/product-mini/platforms/posix/main.c b/product-mini/platforms/posix/main.c index e04745c68..8727ed389 100644 --- a/product-mini/platforms/posix/main.c +++ b/product-mini/platforms/posix/main.c @@ -32,12 +32,28 @@ print_help() #if WASM_ENABLE_LOG != 0 printf(" -v=n Set log verbose level (0 to 5, default is 2) larger\n" " level with more log\n"); +#endif +#if WASM_ENABLE_INTERP != 0 + printf(" --interp Run the wasm app with interpreter mode\n"); +#endif +#if WASM_ENABLE_FAST_JIT != 0 + printf(" --fast-jit Run the wasm app with fast jit mode\n"); +#endif +#if WASM_ENABLE_JIT != 0 + printf(" --llvm-jit Run the wasm app with llvm jit mode\n"); +#endif +#if WASM_ENABLE_JIT != 0 && WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_LAZY_JIT != 0 + printf(" --multi-tier-jit Run the wasm app with multi-tier jit mode\n"); #endif printf(" --stack-size=n Set maximum stack size in bytes, default is 64 KB\n"); printf(" --heap-size=n Set maximum heap size in bytes, default is 16 KB\n"); #if WASM_ENABLE_FAST_JIT != 0 printf(" --jit-codecache-size=n Set fast jit maximum code cache size in bytes,\n"); printf(" default is %u KB\n", FAST_JIT_DEFAULT_CODE_CACHE_SIZE / 1024); +#endif +#if WASM_ENABLE_JIT != 0 + printf(" --llvm-jit-size-level=n Set LLVM JIT size level, default is 3\n"); + printf(" --llvm-jit-opt-level=n Set LLVM JIT optimization level, default is 3\n"); #endif printf(" --repl Start a very simple REPL (read-eval-print-loop) mode\n" " that runs commands in the form of \"FUNC ARG...\"\n"); @@ -67,7 +83,7 @@ print_help() printf(" --module-path= Indicate a module search path. default is current\n" " directory('./')\n"); #endif -#if WASM_ENABLE_LIB_PTHREAD != 0 +#if WASM_ENABLE_LIB_PTHREAD != 0 || WASM_ENABLE_LIB_WASI_THREADS != 0 printf(" --max-threads=n Set maximum thread number per cluster, default is 4\n"); #endif #if WASM_ENABLE_DEBUG_INTERP != 0 @@ -347,9 +363,14 @@ main(int argc, char *argv[]) uint32 stack_size = 64 * 1024, heap_size = 16 * 1024; #if WASM_ENABLE_FAST_JIT != 0 uint32 jit_code_cache_size = FAST_JIT_DEFAULT_CODE_CACHE_SIZE; +#endif +#if WASM_ENABLE_JIT != 0 + uint32 llvm_jit_size_level = 3; + uint32 llvm_jit_opt_level = 3; #endif wasm_module_t wasm_module = NULL; wasm_module_inst_t wasm_module_inst = NULL; + RunningMode running_mode = 0; RuntimeInitArgs init_args; char error_buf[128] = { 0 }; #if WASM_ENABLE_LOG != 0 @@ -387,6 +408,27 @@ main(int argc, char *argv[]) } func_name = argv[0]; } +#if WASM_ENABLE_INTERP != 0 + else if (!strcmp(argv[0], "--interp")) { + running_mode = Mode_Interp; + } +#endif +#if WASM_ENABLE_FAST_JIT != 0 + else if (!strcmp(argv[0], "--fast-jit")) { + running_mode = Mode_Fast_JIT; + } +#endif +#if WASM_ENABLE_JIT != 0 + else if (!strcmp(argv[0], "--llvm-jit")) { + running_mode = Mode_LLVM_JIT; + } +#endif +#if WASM_ENABLE_JIT != 0 && WASM_ENABLE_FAST_JIT != 0 \ + && WASM_ENABLE_LAZY_JIT != 0 + else if (!strcmp(argv[0], "--multi-tier-jit")) { + running_mode = Mode_Multi_Tier_JIT; + } +#endif #if WASM_ENABLE_LOG != 0 else if (!strncmp(argv[0], "-v=", 3)) { log_verbose_level = atoi(argv[0] + 3); @@ -414,6 +456,38 @@ main(int argc, char *argv[]) jit_code_cache_size = atoi(argv[0] + 21); } #endif +#if WASM_ENABLE_JIT != 0 + else if (!strncmp(argv[0], "--llvm-jit-size-level=", 22)) { + if (argv[0][22] == '\0') + return print_help(); + llvm_jit_size_level = atoi(argv[0] + 22); + if (llvm_jit_size_level < 1) { + printf("LLVM JIT size level shouldn't be smaller than 1, " + "setting it to 1\n"); + llvm_jit_size_level = 1; + } + else if (llvm_jit_size_level > 3) { + printf("LLVM JIT size level shouldn't be greater than 3, " + "setting it to 3\n"); + llvm_jit_size_level = 3; + } + } + else if (!strncmp(argv[0], "--llvm-jit-opt-level=", 21)) { + if (argv[0][21] == '\0') + return print_help(); + llvm_jit_opt_level = atoi(argv[0] + 21); + if (llvm_jit_opt_level < 1) { + printf("LLVM JIT opt level shouldn't be smaller than 1, " + "setting it to 1\n"); + llvm_jit_opt_level = 1; + } + else if (llvm_jit_opt_level > 3) { + printf("LLVM JIT opt level shouldn't be greater than 3, " + "setting it to 3\n"); + llvm_jit_opt_level = 3; + } + } +#endif #if WASM_ENABLE_LIBC_WASI != 0 else if (!strncmp(argv[0], "--dir=", 6)) { if (argv[0][6] == '\0') @@ -499,7 +573,7 @@ main(int argc, char *argv[]) } } #endif -#if WASM_ENABLE_LIB_PTHREAD != 0 +#if WASM_ENABLE_LIB_PTHREAD != 0 || WASM_ENABLE_LIB_WASI_THREADS != 0 else if (!strncmp(argv[0], "--max-threads=", 14)) { if (argv[0][14] == '\0') return print_help(); @@ -539,6 +613,7 @@ main(int argc, char *argv[]) memset(&init_args, 0, sizeof(RuntimeInitArgs)); + init_args.running_mode = running_mode; #if WASM_ENABLE_GLOBAL_HEAP_POOL != 0 init_args.mem_alloc_type = Alloc_With_Pool; init_args.mem_alloc_option.pool.heap_buf = global_heap_buf; @@ -554,6 +629,11 @@ main(int argc, char *argv[]) init_args.fast_jit_code_cache_size = jit_code_cache_size; #endif +#if WASM_ENABLE_JIT != 0 + init_args.llvm_jit_size_level = llvm_jit_size_level; + init_args.llvm_jit_opt_level = llvm_jit_opt_level; +#endif + #if WASM_ENABLE_DEBUG_INTERP != 0 init_args.instance_port = instance_port; if (ip_addr) diff --git a/product-mini/platforms/windows/CMakeLists.txt b/product-mini/platforms/windows/CMakeLists.txt index 58e5f384e..35b22a608 100644 --- a/product-mini/platforms/windows/CMakeLists.txt +++ b/product-mini/platforms/windows/CMakeLists.txt @@ -96,10 +96,6 @@ if (WAMR_BUILD_DEBUG_INTERP EQUAL 1) set (WAMR_BUILD_SIMD 0) endif () -if (COLLECT_CODE_COVERAGE EQUAL 1) - set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage") -endif () - set (WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..) include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) diff --git a/product-mini/platforms/windows/main.c b/product-mini/platforms/windows/main.c index 14c255961..05647b5db 100644 --- a/product-mini/platforms/windows/main.c +++ b/product-mini/platforms/windows/main.c @@ -26,9 +26,25 @@ print_help() #if WASM_ENABLE_LOG != 0 printf(" -v=n Set log verbose level (0 to 5, default is 2) larger\n" " level with more log\n"); +#endif +#if WASM_ENABLE_INTERP != 0 + printf(" --interp Run the wasm app with interpreter mode\n"); +#endif +#if WASM_ENABLE_FAST_JIT != 0 + printf(" --fast-jit Run the wasm app with fast jit mode\n"); +#endif +#if WASM_ENABLE_JIT != 0 + printf(" --llvm-jit Run the wasm app with llvm jit mode\n"); +#endif +#if WASM_ENABLE_JIT != 0 && WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_LAZY_JIT != 0 + printf(" --multi-tier-jit Run the wasm app with multi-tier jit mode\n"); #endif printf(" --stack-size=n Set maximum stack size in bytes, default is 64 KB\n"); printf(" --heap-size=n Set maximum heap size in bytes, default is 16 KB\n"); +#if WASM_ENABLE_JIT != 0 + printf(" --llvm-jit-size-level=n Set LLVM JIT size level, default is 3\n"); + printf(" --llvm-jit-opt-level=n Set LLVM JIT optimization level, default is 3\n"); +#endif printf(" --repl Start a very simple REPL (read-eval-print-loop) mode\n" " that runs commands in the form of `FUNC ARG...`\n"); #if WASM_ENABLE_LIBC_WASI != 0 @@ -43,7 +59,7 @@ print_help() printf(" --module-path= Indicate a module search path. default is current\n" " directory('./')\n"); #endif -#if WASM_ENABLE_LIB_PTHREAD != 0 +#if WASM_ENABLE_LIB_PTHREAD != 0 || WASM_ENABLE_LIB_WASI_THREADS != 0 printf(" --max-threads=n Set maximum thread number per cluster, default is 4\n"); #endif #if WASM_ENABLE_DEBUG_INTERP != 0 @@ -228,8 +244,13 @@ main(int argc, char *argv[]) uint8 *wasm_file_buf = NULL; uint32 wasm_file_size; uint32 stack_size = 64 * 1024, heap_size = 16 * 1024; +#if WASM_ENABLE_JIT != 0 + uint32 llvm_jit_size_level = 3; + uint32 llvm_jit_opt_level = 3; +#endif wasm_module_t wasm_module = NULL; wasm_module_inst_t wasm_module_inst = NULL; + RunningMode running_mode = 0; RuntimeInitArgs init_args; char error_buf[128] = { 0 }; #if WASM_ENABLE_LOG != 0 @@ -257,6 +278,26 @@ main(int argc, char *argv[]) } func_name = argv[0]; } +#if WASM_ENABLE_INTERP != 0 + else if (!strcmp(argv[0], "--interp")) { + running_mode = Mode_Interp; + } +#endif +#if WASM_ENABLE_FAST_JIT != 0 + else if (!strcmp(argv[0], "--fast-jit")) { + running_mode = Mode_Fast_JIT; + } +#endif +#if WASM_ENABLE_JIT != 0 + else if (!strcmp(argv[0], "--llvm-jit")) { + running_mode = Mode_LLVM_JIT; + } +#endif +#if WASM_ENABLE_JIT != 0 && WASM_ENABLE_FAST_JIT != 0 + else if (!strcmp(argv[0], "--multi-tier-jit")) { + running_mode = Mode_Multi_Tier_JIT; + } +#endif #if WASM_ENABLE_LOG != 0 else if (!strncmp(argv[0], "-v=", 3)) { log_verbose_level = atoi(argv[0] + 3); @@ -277,6 +318,38 @@ main(int argc, char *argv[]) return print_help(); heap_size = atoi(argv[0] + 12); } +#if WASM_ENABLE_JIT != 0 + else if (!strncmp(argv[0], "--llvm-jit-size-level=", 22)) { + if (argv[0][22] == '\0') + return print_help(); + llvm_jit_size_level = atoi(argv[0] + 22); + if (llvm_jit_size_level < 1) { + printf("LLVM JIT size level shouldn't be smaller than 1, " + "setting it to 1\n"); + llvm_jit_size_level = 1; + } + else if (llvm_jit_size_level > 3) { + printf("LLVM JIT size level shouldn't be greater than 3, " + "setting it to 3\n"); + llvm_jit_size_level = 3; + } + } + else if (!strncmp(argv[0], "--llvm-jit-opt-level=", 21)) { + if (argv[0][21] == '\0') + return print_help(); + llvm_jit_opt_level = atoi(argv[0] + 21); + if (llvm_jit_opt_level < 1) { + printf("LLVM JIT opt level shouldn't be smaller than 1, " + "setting it to 1\n"); + llvm_jit_opt_level = 1; + } + else if (llvm_jit_opt_level > 3) { + printf("LLVM JIT opt level shouldn't be greater than 3, " + "setting it to 3\n"); + llvm_jit_opt_level = 3; + } + } +#endif #if WASM_ENABLE_LIBC_WASI != 0 else if (!strncmp(argv[0], "--dir=", 6)) { if (argv[0][6] == '\0') @@ -317,7 +390,7 @@ main(int argc, char *argv[]) } } #endif -#if WASM_ENABLE_LIB_PTHREAD != 0 +#if WASM_ENABLE_LIB_PTHREAD != 0 || WASM_ENABLE_LIB_WASI_THREADS != 0 else if (!strncmp(argv[0], "--max-threads=", 14)) { if (argv[0][14] == '\0') return print_help(); @@ -357,6 +430,7 @@ main(int argc, char *argv[]) memset(&init_args, 0, sizeof(RuntimeInitArgs)); + init_args.running_mode = running_mode; #if WASM_ENABLE_GLOBAL_HEAP_POOL != 0 init_args.mem_alloc_type = Alloc_With_Pool; init_args.mem_alloc_option.pool.heap_buf = global_heap_buf; @@ -368,6 +442,11 @@ main(int argc, char *argv[]) init_args.mem_alloc_option.allocator.free_func = free; #endif +#if WASM_ENABLE_JIT != 0 + init_args.llvm_jit_size_level = llvm_jit_size_level; + init_args.llvm_jit_opt_level = llvm_jit_opt_level; +#endif + #if WASM_ENABLE_DEBUG_INTERP != 0 init_args.instance_port = instance_port; if (ip_addr) diff --git a/samples/basic/src/native_impl.c b/samples/basic/src/native_impl.c index c85400d12..1374c8dd8 100644 --- a/samples/basic/src/native_impl.c +++ b/samples/basic/src/native_impl.c @@ -7,10 +7,6 @@ #include "wasm_export.h" #include "math.h" -extern bool -wasm_runtime_call_indirect(wasm_exec_env_t exec_env, uint32_t element_indices, - uint32_t argc, uint32_t argv[]); - // The first parameter is not exec_env because it is invoked by native funtions void reverse(char *str, int len) diff --git a/samples/file/wasm-app/main.c b/samples/file/wasm-app/main.c index caf6436d9..f4363475a 100644 --- a/samples/file/wasm-app/main.c +++ b/samples/file/wasm-app/main.c @@ -18,7 +18,7 @@ #define WORLD_OFFSET 7 #define NAME_REPLACMENT "James" #define NAME_REPLACMENT_LEN (sizeof(NAME_REPLACMENT) - 1) -#define ADDITIONAL_SPACE 10 +#define ADDITIONAL_SPACE 1 * 1024 * 1024 int main(int argc, char **argv) @@ -100,7 +100,7 @@ main(int argc, char **argv) printf("[Test] Reading at specified offset passed.\n"); // Test: allocate more space to the file (posix_fallocate) - printf("Allocate more space to the file..\n"); + printf("Allocate more space to the file (posix_fallocate)..\n"); posix_fallocate(fileno(file), ftell(file), ADDITIONAL_SPACE); printf("File current offset: %ld\n", ftell(file)); printf("Moving to the end..\n"); @@ -110,8 +110,8 @@ main(int argc, char **argv) printf("[Test] Allocation or more space passed.\n"); // Test: allocate more space to the file (ftruncate) - printf("Extend the file size of 10 bytes using ftruncate..\n"); - ftruncate(fileno(file), ftell(file) + 10); + printf("Allocate more space to the file (ftruncate)..\n"); + ftruncate(fileno(file), ftell(file) + ADDITIONAL_SPACE); assert(ftell(file) == strlen(text) + ADDITIONAL_SPACE); printf("File current offset: %ld\n", ftell(file)); printf("Moving to the end..\n"); @@ -120,6 +120,31 @@ main(int argc, char **argv) assert(ftell(file) == strlen(text) + 2 * ADDITIONAL_SPACE); printf("[Test] Extension of the file size passed.\n"); + // Test: allocate more space to the file (fseek) + printf("Allocate more space to the file (fseek) from the start..\n"); + printf("File current offset: %ld\n", ftell(file)); + fseek(file, 3 * ADDITIONAL_SPACE, SEEK_SET); + printf("File current offset: %ld\n", ftell(file)); + assert(ftell(file) == 3 * ADDITIONAL_SPACE); + printf("[Test] Extension of the file size passed.\n"); + + // Test: allocate more space to the file (fseek) + printf("Allocate more space to the file (fseek) from the end..\n"); + printf("File current offset: %ld\n", ftell(file)); + fseek(file, ADDITIONAL_SPACE, SEEK_END); + printf("File current offset: %ld\n", ftell(file)); + assert(ftell(file) == 4 * ADDITIONAL_SPACE); + printf("[Test] Extension of the file size passed.\n"); + + // Test: allocate more space to the file (fseek) + printf("Allocate more space to the file (fseek) from the middle..\n"); + fseek(file, 3 * ADDITIONAL_SPACE, SEEK_SET); + printf("File current offset: %ld\n", ftell(file)); + fseek(file, 2 * ADDITIONAL_SPACE, SEEK_CUR); + printf("File current offset: %ld\n", ftell(file)); + assert(ftell(file) == 5 * ADDITIONAL_SPACE); + printf("[Test] Extension of the file size passed.\n"); + // Display some debug information printf("Getting the size of the file on disk..\n"); struct stat st; diff --git a/samples/gui/wasm-apps/increase/Makefile b/samples/gui/wasm-apps/increase/Makefile index 2c150332a..5f250d6ef 100644 --- a/samples/gui/wasm-apps/increase/Makefile +++ b/samples/gui/wasm-apps/increase/Makefile @@ -25,9 +25,9 @@ SRCS += $(APP_FRAMEWORK_DIR)/wgl/app/src/*.c all: @$(CC) $(CFLAGS) $(SRCS) \ - --target=wasm32 -O3 -z stack-size=2048 -Wl,--initial-memory=65536 \ - -Wl,--allow-undefined \ - -Wl,--strip-all,--no-entry -nostdlib \ + --target=wasm32-wasi -O3 -z stack-size=2048 -Wl,--initial-memory=65536 \ + -nostdlib -Wl,--allow-undefined \ + -Wl,--strip-all,--no-entry \ -Wl,--export=on_init -Wl,--export=on_timer_callback \ -Wl,--export=on_widget_event \ -Wl,--export=__heap_base,--export=__data_end \ diff --git a/samples/multi-thread/wasm-apps/CMakeLists.txt b/samples/multi-thread/wasm-apps/CMakeLists.txt index 44ced1cc8..d7352e427 100644 --- a/samples/multi-thread/wasm-apps/CMakeLists.txt +++ b/samples/multi-thread/wasm-apps/CMakeLists.txt @@ -38,3 +38,9 @@ set (CMAKE_EXE_LINKER_FLAGS add_executable(test.wasm main.c) target_link_libraries(test.wasm) + +add_executable(main_thread_exception.wasm main_thread_exception.c) +target_link_libraries(main_thread_exception.wasm) + +add_executable(main_global_atomic.wasm main_global_atomic.c) +target_link_libraries(main_global_atomic.wasm) \ No newline at end of file diff --git a/samples/multi-thread/wasm-apps/main_global_atomic.c b/samples/multi-thread/wasm-apps/main_global_atomic.c new file mode 100644 index 000000000..dafbea886 --- /dev/null +++ b/samples/multi-thread/wasm-apps/main_global_atomic.c @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include + +#define MAX_NUM_THREADS 4 +#define NUM_ITER 1000 + +int g_count = 0; + +static void * +thread(void *arg) +{ + for (int i = 0; i < NUM_ITER; i++) { + __atomic_fetch_add(&g_count, 1, __ATOMIC_SEQ_CST); + } + + return NULL; +} + +int +main(int argc, char **argv) +{ + pthread_t tids[MAX_NUM_THREADS]; + + for (int i = 0; i < MAX_NUM_THREADS; i++) { + if (pthread_create(&tids[i], NULL, thread, NULL) != 0) { + printf("Thread creation failed\n"); + } + } + + for (int i = 0; i < MAX_NUM_THREADS; i++) { + if (pthread_join(tids[i], NULL) != 0) { + printf("Thread join failed\n"); + } + } + + printf("Value of counter after update: %d (expected=%d)\n", g_count, + MAX_NUM_THREADS * NUM_ITER); + if (g_count != MAX_NUM_THREADS * NUM_ITER) { + __builtin_trap(); + } + + return -1; +} \ No newline at end of file diff --git a/samples/multi-thread/wasm-apps/main_thread_exception.c b/samples/multi-thread/wasm-apps/main_thread_exception.c new file mode 100644 index 000000000..80f170d40 --- /dev/null +++ b/samples/multi-thread/wasm-apps/main_thread_exception.c @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include + +typedef struct ThreadArgs { + int start; + int length; +} ThreadArgs; + +void * +thread(void *args) +{ + while (1) { + /* When other threads (including main thread) throw exception, + this thread can successfully exit the dead loop */ + } +} + +int +main() +{ + pthread_t tids; + + if (pthread_create(&tids, NULL, thread, NULL) != 0) { + printf("pthread_create failed\n"); + } + + /* Trigger an exception */ + __builtin_trap(); + + return 0; +} diff --git a/samples/sgx-ra/CMakeLists.txt b/samples/sgx-ra/CMakeLists.txt index 84e4d44c5..7ab552248 100644 --- a/samples/sgx-ra/CMakeLists.txt +++ b/samples/sgx-ra/CMakeLists.txt @@ -39,10 +39,6 @@ set (WAMR_BUILD_FAST_INTERP 1) set (WAMR_BUILD_LIB_RATS 1) # compiling and linking flags -if (COLLECT_CODE_COVERAGE EQUAL 1) - set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage") -endif () - set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu11 -ffunction-sections -fdata-sections \ -Wall -Wno-unused-parameter -Wno-pedantic \ diff --git a/samples/simple/CMakeLists.txt b/samples/simple/CMakeLists.txt index 90d6c2325..f3a0848fe 100644 --- a/samples/simple/CMakeLists.txt +++ b/samples/simple/CMakeLists.txt @@ -12,6 +12,11 @@ message(STATUS "WAMR_BUILD_SDK_PROFILE=${WAMR_BUILD_SDK_PROFILE}") set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") +if ("$ENV{COLLECT_CODE_COVERAGE}" STREQUAL "1" OR COLLECT_CODE_COVERAGE EQUAL 1) + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage") + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") +endif () + set (WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..) ## use library and headers in the SDK diff --git a/samples/simple/build.sh b/samples/simple/build.sh index aab9997e2..4e8155156 100755 --- a/samples/simple/build.sh +++ b/samples/simple/build.sh @@ -56,7 +56,7 @@ done if [ "$CLEAN" = "TRUE" ]; then - rm -rf $CURR_DIR/cmake_build + rm -rf $CURR_DIR/cmake-build fi @@ -107,8 +107,8 @@ cd ${WAMR_DIR}/wamr-sdk echo "#####################build simple project" cd ${CURR_DIR} -mkdir -p cmake_build/$PROFILE -cd cmake_build/$PROFILE +mkdir -p cmake-build/$PROFILE +cd cmake-build/$PROFILE cmake ../.. -DWAMR_BUILD_SDK_PROFILE=$PROFILE $CM_TOOLCHAIN $CM_BUILD_TYPE make if [ $? != 0 ];then diff --git a/samples/simple/sample_test_run.py b/samples/simple/sample_test_run.py index d9e007c01..09c36db5e 100755 --- a/samples/simple/sample_test_run.py +++ b/samples/simple/sample_test_run.py @@ -10,7 +10,17 @@ import subprocess import sys import time import traceback +import glob +WAMRC_CMD = "../../wamr-compiler/build/wamrc" + +def compile_wasm_files_to_aot(wasm_apps_dir): + wasm_files = glob.glob(wasm_apps_dir + "/*.wasm") + print("Compile wasm app into aot files") + for wasm_file in wasm_files: + aot_file = wasm_file[0 : len(wasm_file) - 5] + ".aot"; + cmd = [ WAMRC_CMD, "-o", aot_file, wasm_file ] + subprocess.check_call(cmd) def start_server(cwd): """ @@ -80,8 +90,20 @@ def main(): """ parser = argparse.ArgumentParser(description="run the sample and examine outputs") parser.add_argument("working_directory", type=str) + parser.add_argument("--aot", action='store_true', help="Test with AOT") args = parser.parse_args() + test_aot = False + suffix = ".wasm" + if not args.aot: + print("Test with interpreter mode") + else: + print("Test with AOT mode") + test_aot = True + suffix = ".aot" + wasm_apps_dir = args.working_directory + "/wasm-apps" + compile_wasm_files_to_aot(wasm_apps_dir) + ret = 1 app_server = None try: @@ -90,35 +112,47 @@ def main(): # wait for a second time.sleep(1) - print("--> Install timer.wasm...") + print("--> Install timer" + suffix + "...") install_wasm_application( - "timer", "./wasm-apps/timer.wasm", args.working_directory + "timer", "./wasm-apps/timer" + suffix, args.working_directory ) - print("--> Install event_publisher.wasm...") + # wait for a second + time.sleep(3) + + print("--> Query all installed applications...") + query_installed_application(args.working_directory) + + print("--> Install event_publisher" + suffix + "...") install_wasm_application( "event_publisher", - "./wasm-apps/event_publisher.wasm", + "./wasm-apps/event_publisher" + suffix, args.working_directory, ) - print("--> Install event_subscriber.wasm...") + print("--> Install event_subscriber" + suffix + "...") install_wasm_application( "event_subscriber", - "./wasm-apps/event_subscriber.wasm", + "./wasm-apps/event_subscriber" + suffix, args.working_directory, ) - print("--> Uninstall timer.wasm...") + print("--> Query all installed applications...") + query_installed_application(args.working_directory) + + print("--> Uninstall timer" + suffix + "...") uninstall_wasm_application("timer", args.working_directory) - print("--> Uninstall event_publisher.wasm...") + print("--> Query all installed applications...") + query_installed_application(args.working_directory) + + print("--> Uninstall event_publisher" + suffix + "...") uninstall_wasm_application( "event_publisher", args.working_directory, ) - print("--> Uninstall event_subscriber.wasm...") + print("--> Uninstall event_subscriber" + suffix + "...") uninstall_wasm_application( "event_subscriber", args.working_directory, @@ -127,26 +161,55 @@ def main(): print("--> Query all installed applications...") query_installed_application(args.working_directory) - print("--> Install request_handler.wasm...") + print("--> Install request_handler" + suffix + "...") install_wasm_application( "request_handler", - "./wasm-apps/request_handler.wasm", + "./wasm-apps/request_handler" + suffix, args.working_directory, ) print("--> Query again...") query_installed_application(args.working_directory) - print("--> Install request_sender.wasm...") + print("--> Install request_sender" + suffix + "...") install_wasm_application( "request_sender", - "./wasm-apps/request_sender.wasm", + "./wasm-apps/request_sender" + suffix, args.working_directory, ) print("--> Send GET to the Wasm application named request_handler...") send_get_to_wasm_application("request_handler", "/url1", args.working_directory) + print("--> Uninstall request_handler" + suffix + "...") + uninstall_wasm_application( + "request_handler", + args.working_directory, + ) + + print("--> Uninstall request_sender" + suffix + "...") + uninstall_wasm_application( + "request_sender", + args.working_directory, + ) + + # Install a wasm app named "__exit_app_manager__" just to make app manager exit + # while the wasm app is uninstalled, so as to collect the code coverage data. + # Only available when collecting code coverage is enabled. + print("--> Install timer" + suffix + "...") + install_wasm_application( + "__exit_app_manager__", "./wasm-apps/timer" + suffix, args.working_directory + ) + + print("--> Uninstall timer" + suffix + "...") + uninstall_wasm_application( + "__exit_app_manager__", + args.working_directory, + ) + + # wait for a second + time.sleep(1) + print("--> All pass") ret = 0 except AssertionError: diff --git a/samples/socket-api/README.md b/samples/socket-api/README.md index a838c8a6e..a3bc5ac15 100644 --- a/samples/socket-api/README.md +++ b/samples/socket-api/README.md @@ -68,7 +68,7 @@ Say Hi from the Server send and receive data via 127.0.0.1:1234. ```bash -$ ./iwasm --addr-pool=127.0.0.1/0 ./send_recv.wasm +$ ./iwasm --addr-pool=127.0.0.1/0 ./send_recv.wasm ``` The output is: @@ -121,7 +121,7 @@ Shuting down ``` ```bash -$ ./iwasm --addr-pool=127.0.0.1/15 --heap-size=10000000 timeout_client.wasm +$ ./iwasm --addr-pool=127.0.0.1/15 timeout_client.wasm ``` The output is: @@ -164,7 +164,7 @@ Datagram sent `addr_resolve.wasm` demonstrates the usage of resolving a domain name ``` -$ ./iwasm --allow-resolve=*.com addr_resolve.wasm github.com +$ ./iwasm --allow-resolve=*.com addr_resolve.wasm github.com ``` The command displays the host name and its corresponding IP address: diff --git a/samples/socket-api/sample_test_run.py b/samples/socket-api/sample_test_run.py new file mode 100755 index 000000000..ec0060281 --- /dev/null +++ b/samples/socket-api/sample_test_run.py @@ -0,0 +1,141 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2023 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +import argparse +import shlex +import subprocess +import sys +import time +import traceback +import glob + +WAMRC_CMD = "../../wamr-compiler/build/wamrc" + +def compile_wasm_files_to_aot(wasm_apps_dir): + wasm_files = glob.glob(wasm_apps_dir + "/*.wasm") + print("Compile wasm app into aot files") + for wasm_file in wasm_files: + aot_file = wasm_file[0 : len(wasm_file) - 5] + ".aot"; + cmd = [ WAMRC_CMD, "-o", aot_file, wasm_file ] + subprocess.check_call(cmd) + +def start_server(cmd, cwd): + app_server = subprocess.Popen(shlex.split(cmd), cwd=cwd) + return app_server + +def run_cmd(cmd, cwd): + qry_prc = subprocess.run( + shlex.split(cmd), cwd=cwd, check=False, capture_output=True + ) + if (qry_prc.returncode != 0): + print("Run {} failed, return {}".format(cmd), qry_prc.returncode) + return + print("return code: {}, output:\n{}".format(qry_prc.returncode, + qry_prc.stdout.decode())) + +def main(): + """ + GO!GO!!GO!!! + """ + parser = argparse.ArgumentParser(description="run the sample and examine outputs") + parser.add_argument("working_directory", type=str) + parser.add_argument("--aot", action='store_true', help="Test with AOT") + args = parser.parse_args() + + test_aot = False + suffix = ".wasm" + if not args.aot: + print("Test with interpreter mode") + else: + print("Test with AOT mode") + test_aot = True + suffix = ".aot" + wasm_apps_dir = args.working_directory + compile_wasm_files_to_aot(wasm_apps_dir) + + ret = 1 + app_server = None + try: + print("\n================================") + print("Test TCP server and client") + cmd = "./iwasm --addr-pool=0.0.0.0/15 tcp_server" + suffix + app_server = start_server(cmd, args.working_directory) + # wait for a second + time.sleep(1) + cmd = "./iwasm --addr-pool=127.0.0.1/15 tcp_client" + suffix + for i in range(5): + run_cmd(cmd, args.working_directory) + + print("\n================================") + print("Test UDP server and client") + cmd = "./iwasm --addr-pool=0.0.0.0/15 udp_server" + suffix + app_server = start_server(cmd, args.working_directory) + # wait for a second + time.sleep(1) + cmd = "./iwasm --addr-pool=127.0.0.1/15 udp_client" + suffix + for i in range(5): + run_cmd(cmd, args.working_directory) + + print("\n=====================================================") + print("Sleep 80 seconds to wait TCP server port actually close") + time.sleep(80) + + print("\n================================") + print("Test send and receive") + cmd = "./iwasm --addr-pool=127.0.0.1/0 ./send_recv" + suffix + run_cmd(cmd, args.working_directory) + + print("\n================================") + print("Test socket options") + cmd = "./iwasm socket_opts" + suffix + run_cmd(cmd, args.working_directory) + + print("\n================================") + print("Test timeout server and client") + cmd = "./iwasm --addr-pool=0.0.0.0/15 timeout_server" + suffix + app_server = start_server(cmd, args.working_directory) + # wait for a second + time.sleep(1) + cmd = "./iwasm --addr-pool=127.0.0.1/15 timeout_client" + suffix + run_cmd(cmd, args.working_directory) + + print("\n==========================================") + print("Test multicast_client and multicast_server") + cmd = "./iwasm --addr-pool=0.0.0.0/0,::/0 multicast_client.wasm 224.0.0.1" + app_server = start_server(cmd, args.working_directory) + # wait for a second + time.sleep(1) + cmd = "./multicast_server 224.0.0.1" + run_cmd(cmd, args.working_directory) + + cmd = "./iwasm --addr-pool=0.0.0.0/0,::/0 multicast_client.wasm FF02:113D:6FDD:2C17:A643:FFE2:1BD1:3CD2" + app_server = start_server(cmd, args.working_directory) + # wait for a second + time.sleep(1) + cmd = "./multicast_server FF02:113D:6FDD:2C17:A643:FFE2:1BD1:3CD2" + run_cmd(cmd, args.working_directory) + + print("\n================================") + print("Test address resolving") + cmd = "./iwasm --allow-resolve=*.com addr_resolve.wasm github.com" + cmd = "./multicast_server FF02:113D:6FDD:2C17:A643:FFE2:1BD1:3CD2" + run_cmd(cmd, args.working_directory) + + # wait for a second + time.sleep(1) + + print("--> All pass") + ret = 0 + except AssertionError: + traceback.print_exc() + finally: + app_server.kill() + + return ret + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/samples/socket-api/wasm-src/CMakeLists.txt b/samples/socket-api/wasm-src/CMakeLists.txt index fda5d3083..8f11e0a6b 100644 --- a/samples/socket-api/wasm-src/CMakeLists.txt +++ b/samples/socket-api/wasm-src/CMakeLists.txt @@ -54,7 +54,9 @@ function(COMPILE_WITH_CLANG SOURCE_FILE) target_link_options(${MAIN_TARGET_NAME} PRIVATE LINKER:--export=__heap_base LINKER:--export=__data_end - LINKER:--shared-memory,--max-memory=196608 + LINKER:--export=malloc + LINKER:--export=free + LINKER:--shared-memory,--max-memory=10485760 LINKER:--no-check-features LINKER:--allow-undefined ) diff --git a/samples/socket-api/wasm-src/send_recv.c b/samples/socket-api/wasm-src/send_recv.c index 169dc471e..0071b2a7b 100644 --- a/samples/socket-api/wasm-src/send_recv.c +++ b/samples/socket-api/wasm-src/send_recv.c @@ -19,6 +19,7 @@ static pthread_mutex_t lock = { 0 }; static pthread_cond_t cond = { 0 }; +static bool server_create_failed = false; static bool server_is_ready = false; void * @@ -46,6 +47,8 @@ run_as_server(void *arg) pthread_mutex_lock(&lock); sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { + server_create_failed = true; + pthread_cond_signal(&cond); pthread_mutex_unlock(&lock); perror("Create a socket failed"); return NULL; @@ -53,6 +56,8 @@ run_as_server(void *arg) #ifndef __wasi__ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on))) { + server_create_failed = true; + pthread_cond_signal(&cond); pthread_mutex_unlock(&lock); perror("Setsockopt failed"); goto fail1; @@ -66,12 +71,16 @@ run_as_server(void *arg) addrlen = sizeof(addr); if (bind(sock, (struct sockaddr *)&addr, addrlen) < 0) { + server_create_failed = true; + pthread_cond_signal(&cond); pthread_mutex_unlock(&lock); perror("Bind failed"); goto fail1; } if (listen(sock, 0) < 0) { + server_create_failed = true; + pthread_cond_signal(&cond); pthread_mutex_unlock(&lock); perror("Listen failed"); goto fail1; @@ -117,11 +126,15 @@ run_as_client(void *arg) ssize_t recv_len = 0; pthread_mutex_lock(&lock); - while (false == server_is_ready) { + while (!server_create_failed && !server_is_ready) { pthread_cond_wait(&cond, &lock); } pthread_mutex_unlock(&lock); + if (server_create_failed) { + return NULL; + } + printf("Client is running...\n"); sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { diff --git a/samples/wasi-threads/CMakeLists.txt b/samples/wasi-threads/CMakeLists.txt new file mode 100644 index 000000000..93f6b3310 --- /dev/null +++ b/samples/wasi-threads/CMakeLists.txt @@ -0,0 +1,87 @@ +# Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 3.14) + +include(CheckPIESupported) + +project(wasi_threads_sample) + +if (NOT DEFINED WASI_SYSROOT) + message (WARNING "Custom sysroot with threads enabled is required to build wasi threads samples. +Please note that current wasi-sdk doesn't ship with threads enabled. +Run cmake command with -DWASI_SYSROOT=/path/to/sysroot/with/threads to compile samples.") + return () +endif () + +################ runtime settings ################ +string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM) +if (APPLE) + add_definitions(-DBH_PLATFORM_DARWIN) +endif () + +# Resetdefault linker flags +set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") + +# WAMR features switch + +# Set WAMR_BUILD_TARGET, currently values supported: +# "X86_64", "AMD_64", "X86_32", "AARCH64[sub]", "ARM[sub]", "THUMB[sub]", +# "MIPS", "XTENSA", "RISCV64[sub]", "RISCV32[sub]" +if (NOT DEFINED WAMR_BUILD_TARGET) + if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm64|aarch64)") + set (WAMR_BUILD_TARGET "AARCH64") + elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv64") + set (WAMR_BUILD_TARGET "RISCV64") + elseif (CMAKE_SIZEOF_VOID_P EQUAL 8) + # Build as X86_64 by default in 64-bit platform + set (WAMR_BUILD_TARGET "X86_64") + elseif (CMAKE_SIZEOF_VOID_P EQUAL 4) + # Build as X86_32 by default in 32-bit platform + set (WAMR_BUILD_TARGET "X86_32") + else () + message(SEND_ERROR "Unsupported build target platform!") + endif () +endif () + +if (NOT CMAKE_BUILD_TYPE) + set (CMAKE_BUILD_TYPE Release) +endif () + +set(WAMR_BUILD_INTERP 1) +set(WAMR_BUILD_AOT 1) +set(WAMR_BUILD_JIT 0) +set(WAMR_BUILD_LIBC_BUILTIN 1) +set(WAMR_BUILD_FAST_INTERP 1) +set(WAMR_BUILD_LIBC_WASI 1) +set(WAMR_BUILD_LIB_WASI_THREADS 1) + +# compiling and linking flags +if (NOT (CMAKE_C_COMPILER MATCHES ".*clang.*" OR CMAKE_C_COMPILER_ID MATCHES ".*Clang")) + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") +endif () +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wformat -Wformat-security") + +# build out vmlib +set(WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..) +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) +################################################ + + +################ wasm application ################ +add_subdirectory(wasm-apps) + +################ wamr runtime ################ +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) + +set (RUNTIME_SOURCE_ALL + ${CMAKE_CURRENT_LIST_DIR}/../../product-mini/platforms/linux/main.c + ${UNCOMMON_SHARED_SOURCE} +) +add_executable (iwasm ${RUNTIME_SOURCE_ALL}) +check_pie_supported() +set_target_properties (iwasm PROPERTIES POSITION_INDEPENDENT_CODE ON) +target_link_libraries(iwasm vmlib -lpthread -lm -ldl) diff --git a/samples/wasi-threads/README.md b/samples/wasi-threads/README.md new file mode 100644 index 000000000..ca8d166d2 --- /dev/null +++ b/samples/wasi-threads/README.md @@ -0,0 +1,32 @@ +# "WASI threads" sample introduction + +Currently, since the `wasi-sdk` does not have thread support in the latest release, make sure to have [wasi-libc](https://github.com/WebAssembly/wasi-libc) installed. Build it with threads enabled, e.g. + +```shell +make \ + AR=/opt/wasi-sdk/bin/llvm-ar \ + NM=/opt/wasi-sdk/bin/llvm-nm \ + CC=/opt/wasi-sdk/bin/clang \ + THREAD_MODEL=posix +``` + +## Build and run the samples + +```shell +$ mkdir build +$ cd build +$ cmake -DWASI_SYSROOT=/path/to/wasi-libc/sysroot .. +$ make +... +$ ./iwasm wasm-apps/no_pthread.wasm +... +$ ./iwasm wasm-apps/exception_propagation.wasm +``` + +## Run samples in AOT mode +```shell +$ ../../../wamr-compiler/build/wamrc \ + --enable-multi-thread \ + -o wasm-apps/no_pthread.aot wasm-apps/no_pthread.wasm +$ ./iwasm wasm-apps/no_pthread.aot +``` diff --git a/samples/wasi-threads/wasm-apps/CMakeLists.txt b/samples/wasi-threads/wasm-apps/CMakeLists.txt new file mode 100644 index 000000000..e77e53304 --- /dev/null +++ b/samples/wasi-threads/wasm-apps/CMakeLists.txt @@ -0,0 +1,44 @@ +# Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +if (APPLE) + set (HAVE_FLAG_SEARCH_PATHS_FIRST 0) + set (CMAKE_C_LINK_FLAGS "") + set (CMAKE_CXX_LINK_FLAGS "") +endif () + +if (NOT DEFINED WASI_SDK_DIR) + set (WASI_SDK_DIR "/opt/wasi-sdk") +endif () + +set (CMAKE_SYSROOT "${WASI_SYSROOT}") +set (CMAKE_C_COMPILER "${WASI_SDK_DIR}/bin/clang") +set (CMAKE_ASM_COMPILER "${WASI_SDK_DIR}/bin/clang") +set (CMAKE_EXE_LINKER_FLAGS "-target wasm32-wasi-threads") + +if ("$ENV{COLLECT_CODE_COVERAGE}" STREQUAL "1" OR COLLECT_CODE_COVERAGE EQUAL 1) + set (CMAKE_C_FLAGS "") + set (CMAKE_CXX_FLAGS "") +endif () + +function (compile_sample SOURCE_FILE) + get_filename_component (FILE_NAME ${SOURCE_FILE} NAME_WLE) + set (WASM_MODULE ${FILE_NAME}.wasm) + add_executable (${WASM_MODULE} ${SOURCE_FILE} ${ARGN}) + + target_compile_options (${WASM_MODULE} PRIVATE + -pthread -ftls-model=local-exec) + + target_link_options (${WASM_MODULE} PRIVATE + -z stack-size=32768 + LINKER:--export=__heap_base + LINKER:--export=__data_end + LINKER:--shared-memory,--max-memory=1966080 + LINKER:--export=wasi_thread_start + LINKER:--export=malloc + LINKER:--export=free + ) +endfunction () + +compile_sample(no_pthread.c wasi_thread_start.S) +compile_sample(thread_termination.c wasi_thread_start.S) diff --git a/samples/wasi-threads/wasm-apps/no_pthread.c b/samples/wasi-threads/wasm-apps/no_pthread.c new file mode 100644 index 000000000..dc3c95530 --- /dev/null +++ b/samples/wasi-threads/wasm-apps/no_pthread.c @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2022 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +#ifndef __wasi__ +#error This example only compiles to WASM/WASI target +#endif + +#include +#include +#include + +#include "wasi_thread_start.h" + +static const int64_t SECOND = 1000 * 1000 * 1000; + +typedef struct { + start_args_t base; + int th_ready; + int value; + int thread_id; +} shared_t; + +void +__wasi_thread_start_C(int thread_id, int *start_arg) +{ + shared_t *data = (shared_t *)start_arg; + + printf("New thread ID: %d, starting parameter: %d\n", thread_id, + data->value); + + data->thread_id = thread_id; + data->value += 8; + printf("Updated value: %d\n", data->value); + + __atomic_store_n(&data->th_ready, 1, __ATOMIC_SEQ_CST); + __builtin_wasm_memory_atomic_notify(&data->th_ready, 1); +} + +int +main(int argc, char **argv) +{ + shared_t data = { { NULL }, 0, 52, -1 }; + int thread_id; + int ret = EXIT_SUCCESS; + + if (!start_args_init(&data.base)) { + printf("Stack allocation for thread failed\n"); + return EXIT_FAILURE; + } + + thread_id = __wasi_thread_spawn(&data); + if (thread_id < 0) { + printf("Failed to create thread: %d\n", thread_id); + ret = EXIT_FAILURE; + goto final; + } + + if (__builtin_wasm_memory_atomic_wait32(&data.th_ready, 0, SECOND) == 2) { + printf("Timeout\n"); + ret = EXIT_FAILURE; + goto final; + } + + printf("Thread completed, new value: %d, thread id: %d\n", data.value, + data.thread_id); + + assert(thread_id == data.thread_id); + +final: + start_args_deinit(&data.base); + + return ret; +} diff --git a/samples/wasi-threads/wasm-apps/thread_termination.c b/samples/wasi-threads/wasm-apps/thread_termination.c new file mode 100644 index 000000000..9f5cf1fe8 --- /dev/null +++ b/samples/wasi-threads/wasm-apps/thread_termination.c @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2022 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +#ifndef __wasi__ +#error This example only compiles to WASM/WASI target +#endif + +#include +#include +#include +#include +#include +#include + +#include "wasi_thread_start.h" + +#define BUSY_WAIT 0 +#define ATOMIC_WAIT 1 +#define POLL_ONEOFF 2 + +/* Change parameters here to modify the sample behavior */ +#define TEST_TERMINATION_BY_TRAP 0 /* Otherwise `proc_exit` termination */ +#define TEST_TERMINATION_IN_MAIN_THREAD 1 /* Otherwise in spawn thread */ +#define LONG_TASK_IMPL ATOMIC_WAIT + +#define TIMEOUT_SECONDS 10 +#define NUM_THREADS 3 +static pthread_barrier_t barrier; + +typedef struct { + start_args_t base; + bool throw_exception; +} shared_t; + +void +run_long_task() +{ +#if LONG_TASK_IMPL == BUSY_WAIT + for (int i = 0; i < TIMEOUT_SECONDS; i++) + sleep(1); +#elif LONG_TASK_IMPL == ATOMIC_WAIT + __builtin_wasm_memory_atomic_wait32(0, 0, -1); +#else + sleep(TIMEOUT_SECONDS); +#endif +} + +void +start_job() +{ + /* Wait for all threads (including the main thread) to be ready */ + pthread_barrier_wait(&barrier); + run_long_task(); /* Task to be interrupted */ + assert(false && "Thread termination test failed"); +} + +void +terminate_process() +{ + /* Wait for all other threads (including main thread) to be ready */ + printf("Waiting before terminating\n"); + pthread_barrier_wait(&barrier); + + printf("Force termination\n"); +#if TEST_TERMINATION_BY_TRAP == 1 + __builtin_trap(); +#else + __wasi_proc_exit(33); +#endif +} + +void +__wasi_thread_start_C(int thread_id, int *start_arg) +{ + shared_t *data = (shared_t *)start_arg; + + if (data->throw_exception) { + terminate_process(); + } + else { + printf("Thread running\n"); + + start_job(); + } +} + +int +main(int argc, char **argv) +{ + int thread_id = -1, i; + shared_t data[NUM_THREADS] = { 0 }; + + if (pthread_barrier_init(&barrier, NULL, NUM_THREADS + 1) != 0) { + printf("Failed to init barrier\n"); + return EXIT_FAILURE; + } + + for (i = 0; i < NUM_THREADS; i++) { + /* No graceful memory free to simplify the example */ + if (!start_args_init(&data[i].base)) { + printf("Failed to allocate thread's stack\n"); + return EXIT_FAILURE; + } + } + + /* Create a thread that forces termination through trap or `proc_exit` */ +#if TEST_TERMINATION_IN_MAIN_THREAD == 1 + data[0].throw_exception = false; +#else + data[0].throw_exception = true; +#endif + thread_id = __wasi_thread_spawn(&data[0]); + if (thread_id < 0) { + printf("Failed to create thread: %d\n", thread_id); + return EXIT_FAILURE; + } + + /* Create two additional threads to test exception propagation */ + data[1].throw_exception = false; + thread_id = __wasi_thread_spawn(&data[1]); + if (thread_id < 0) { + printf("Failed to create thread: %d\n", thread_id); + return EXIT_FAILURE; + } + data[2].throw_exception = false; + thread_id = __wasi_thread_spawn(&data[2]); + if (thread_id < 0) { + printf("Failed to create thread: %d\n", thread_id); + return EXIT_FAILURE; + } + +#if TEST_TERMINATION_IN_MAIN_THREAD == 1 + printf("Force termination (main thread)\n"); + terminate_process(); +#else /* TEST_TERMINATION_IN_MAIN_THREAD */ + printf("Main thread running\n"); + + start_job(); +#endif /* TEST_TERMINATION_IN_MAIN_THREAD */ + return EXIT_SUCCESS; +} diff --git a/samples/wasi-threads/wasm-apps/wasi_thread_start.S b/samples/wasi-threads/wasm-apps/wasi_thread_start.S new file mode 100644 index 000000000..ea8fd1400 --- /dev/null +++ b/samples/wasi-threads/wasm-apps/wasi_thread_start.S @@ -0,0 +1,22 @@ +# A slightly modified copy of the wasi-libc implementation +# https://github.com/WebAssembly/wasi-libc/pull/376/ + .globaltype __stack_pointer, i32 + .functype __wasi_thread_start_C (i32, i32) -> () + + .globl wasi_thread_start + +wasi_thread_start: + .functype wasi_thread_start (i32, i32) -> () + + # Set up the minimum C environment. + # Note: offsetof(start_arg, stack) == 0 + local.get 1 # start_arg + i32.load 0 # stack + global.set __stack_pointer + + # Make the C function do the rest of work. + local.get 0 # tid + local.get 1 # start_arg + call __wasi_thread_start_C + + end_function \ No newline at end of file diff --git a/samples/wasi-threads/wasm-apps/wasi_thread_start.h b/samples/wasi-threads/wasm-apps/wasi_thread_start.h new file mode 100644 index 000000000..a46917d0a --- /dev/null +++ b/samples/wasi-threads/wasm-apps/wasi_thread_start.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2022 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +#ifndef WASI_THREAD_START_H +#define WASI_THREAD_START_H + +#define STACK_SIZE 32 * 1024 // same as the main stack + +typedef struct { + void *stack; +} start_args_t; + +static inline int +start_args_init(start_args_t *start_args) +{ + start_args->stack = malloc(STACK_SIZE); + if (!start_args->stack) { + return 0; + } + + start_args->stack += STACK_SIZE; + return 1; +} + +static inline void +start_args_deinit(start_args_t *start_args) +{ + free(start_args->stack - STACK_SIZE); +} + +#endif \ No newline at end of file diff --git a/samples/wasm-c-api-imports/.gitignore b/samples/wasm-c-api-imports/.gitignore new file mode 100644 index 000000000..ab998a6eb --- /dev/null +++ b/samples/wasm-c-api-imports/.gitignore @@ -0,0 +1,2 @@ +/wasm/inc/** +!/wasm/inc/.* diff --git a/samples/wasm-c-api-imports/CMakeLists.txt b/samples/wasm-c-api-imports/CMakeLists.txt new file mode 100644 index 000000000..1325e110c --- /dev/null +++ b/samples/wasm-c-api-imports/CMakeLists.txt @@ -0,0 +1,174 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 3.14) +project(how-to-deal-with-import) + +include(CMakePrintHelpers) +include(CTest) +include(ExternalProject) +include(FetchContent) + +# +# dependencies +# +set(WAMR_ROOT ${CMAKE_CURRENT_LIST_DIR}/../../) +# wasm required headers +execute_process( + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${WARM_ROOT}/${WAMR_ROOT}/wamr-sdk/app/libc-builtin-sysroot/include/pthread.h + ${CMAKE_CURRENT_LIST_DIR}/wasm/inc +) + +# vmlib +################ runtime settings ################ +string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM) +if (APPLE) + add_definitions(-DBH_PLATFORM_DARWIN) +endif () + +# Resetdefault linker flags +set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") + +# WAMR features switch + +# Set WAMR_BUILD_TARGET, currently values supported: +# "X86_64", "AMD_64", "X86_32", "AARCH64[sub]", "ARM[sub]", "THUMB[sub]", +# "MIPS", "XTENSA", "RISCV64[sub]", "RISCV32[sub]" +if (NOT DEFINED WAMR_BUILD_TARGET) + if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm64|aarch64)") + set (WAMR_BUILD_TARGET "AARCH64") + elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv64") + set (WAMR_BUILD_TARGET "RISCV64") + elseif (CMAKE_SIZEOF_VOID_P EQUAL 8) + # Build as X86_64 by default in 64-bit platform + set (WAMR_BUILD_TARGET "X86_64") + elseif (CMAKE_SIZEOF_VOID_P EQUAL 4) + # Build as X86_32 by default in 32-bit platform + set (WAMR_BUILD_TARGET "X86_32") + else () + message(SEND_ERROR "Unsupported build target platform!") + endif () +endif () + +if (NOT CMAKE_BUILD_TYPE) + set (CMAKE_BUILD_TYPE Release) +endif () + +if (NOT DEFINED WAMR_BUILD_AOT) + # Enable AOT by default. + set (WAMR_BUILD_AOT 1) +endif () +if (NOT DEFINED WAMR_BUILD_INTERP) + # Disable Interpreter by default + set (WAMR_BUILD_INTERP 0) +endif () +set(WAMR_BUILD_JIT 0) +set(WAMR_BUILD_FAST_INTERP 1) +set(WAMR_BUILD_LIB_PTHREAD 1) +set(WAMR_BUILD_LIBC_BUILTIN 1) +set(WAMR_BUILD_LIBC_WASI 1) +set(WAMR_BUILD_SIMD 0) + +# compiling and linking flags +if (NOT (CMAKE_C_COMPILER MATCHES ".*clang.*" OR CMAKE_C_COMPILER_ID MATCHES ".*Clang")) + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") +endif () +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wformat -Wformat-security") + +# build out vmlib +set(WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..) +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) +target_link_libraries(vmlib INTERFACE dl m pthread) +if(WAMR_BUILD_AOT EQUAL 1) + target_compile_definitions(vmlib INTERFACE -DWASM_ENABLE_AOT=1) +else() + target_compile_definitions(vmlib INTERFACE -DWASM_ENABLE_AOT=0) +endif() + +if(WAMR_BUILD_INTERP EQUAL 1) + target_compile_definitions(vmlib INTERFACE -DWASM_ENABLE_INTERP=1) +else() + target_compile_definitions(vmlib INTERFACE -DWASM_ENABLE_INTERP=0) +endif() + +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + # ASAN + UBSAN + target_compile_options(vmlib INTERFACE -fsanitize=address,undefined) + target_link_options(vmlib INTERFACE -fsanitize=address,undefined) +endif() + +# # MSAN +# target_compile_options(vmlib INTERFACE -fsanitize=memory -fno-optimize-sibling-calls -fsanitize-memory-track-origins=2 -fno-omit-frame-pointer) +# target_link_options(vmlib INTERFACE -fsanitize=memory) + +# wamrc +if(WAMR_BUILD_AOT EQUAL 1 AND WAMR_BUILD_INTERP EQUAL 0) + ExternalProject_Add(wamrc + PREFIX wamrc-build + SOURCE_DIR ${WAMR_ROOT}/wamr-compiler + CONFIGURE_COMMAND ${CMAKE_COMMAND} -S ${WAMR_ROOT}/wamr-compiler -B build + BUILD_COMMAND ${CMAKE_COMMAND} --build build --target wamrc + INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_if_different build/wamrc ${CMAKE_CURRENT_BINARY_DIR}/wamrc + ) +endif() + +# +# host +add_subdirectory(host) +add_custom_target( + install_host ALL + COMMAND ${CMAKE_COMMAND} -E copy_if_different ./host/example1 . + DEPENDS example1 + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +) + +# TODO: replace it with a find_package() +set(WASI_SDK_DIR /opt/wasi-sdk-19.0/) +set(WASI_TOOLCHAIN_FILE ${WASI_SDK_DIR}/share/cmake/wasi-sdk.cmake) +set(WASI_SYS_ROOT ${WASI_SDK_DIR}/share/wasi-sysroot) + +# +# wasm +if(WAMR_BUILD_AOT EQUAL 1 AND WAMR_BUILD_INTERP EQUAL 0) + ExternalProject_Add(wasm + PREFIX wasm-build + DEPENDS wamrc + BUILD_ALWAYS TRUE + SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/wasm + CONFIGURE_COMMAND ${CMAKE_COMMAND} -S ${CMAKE_CURRENT_LIST_DIR}/wasm -B build + -DWASI_SDK_PREFIX=${WASI_SDK_DIR} + -DCMAKE_TOOLCHAIN_FILE=${WASI_TOOLCHAIN_FILE} + -DCMAKE_SYSROOT=${WASI_SYS_ROOT} + -DWASM_TO_AOT=ON + -DWAMRC_PATH=${CMAKE_CURRENT_BINARY_DIR}/wamrc + -DSOCKET_WASI_CMAKE=${WAMR_ROOT}/core/iwasm/libraries/lib-socket/lib_socket_wasi.cmake + BUILD_COMMAND ${CMAKE_COMMAND} --build build + INSTALL_COMMAND ${CMAKE_COMMAND} --install build --prefix ${CMAKE_CURRENT_BINARY_DIR} + ) +else() + ExternalProject_Add(wasm + PREFIX wasm-build + BUILD_ALWAYS TRUE + SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/wasm + CONFIGURE_COMMAND ${CMAKE_COMMAND} -S ${CMAKE_CURRENT_LIST_DIR}/wasm -B build + -DWASI_SDK_PREFIX=${WASI_SDK_DIR} + -DCMAKE_TOOLCHAIN_FILE=${WASI_TOOLCHAIN_FILE} + -DCMAKE_SYSROOT=${WASI_SYS_ROOT} + -DSOCKET_WASI_CMAKE=${WAMR_ROOT}/core/iwasm/libraries/lib-socket/lib_socket_wasi.cmake + BUILD_COMMAND ${CMAKE_COMMAND} --build build + INSTALL_COMMAND ${CMAKE_COMMAND} --install build --prefix ${CMAKE_CURRENT_BINARY_DIR} + ) +endif() + +# +# Test +# +add_test( + NAME run_example1 + COMMAND ./example1 + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +) diff --git a/samples/wasm-c-api-imports/README.md b/samples/wasm-c-api-imports/README.md new file mode 100644 index 000000000..9b61a6e74 --- /dev/null +++ b/samples/wasm-c-api-imports/README.md @@ -0,0 +1,174 @@ +# How to create `imports` for wasm_instance_new() properly + +It's always been asked how to create `wasm_extern_vec_t *imports` for +`wasm_instance_new()`? + +```c +WASM_API_EXTERN own wasm_instance_t* wasm_instance_new( + wasm_store_t*, const wasm_module_t*, const wasm_extern_vec_t *imports, + own wasm_trap_t** trap +); +``` + +`wasm_extern_vec_t *imports` is required to match the requirement of _the +import section_ of a .wasm. + +```bash +$ /opt/wabt-1.0.31/bin/wasm-objdump -j Import -x .wasm + +Section Details: + +Import[27]: + - func[0] sig=2 <- env.pthread_mutex_lock + - func[1] sig=2 <- env.pthread_mutex_unlock + - func[2] sig=2 <- env.pthread_cond_signal + - func[3] sig=3 <- env.log + ... + - func[11] sig=4 <__imported_wasi_snapshot_preview1_sock_bind> <- wasi_snapshot_preview1.sock_bind + - func[12] sig=4 <__imported_wasi_snapshot_preview1_sock_connect> <- wasi_snapshot_preview1.sock_connect + - func[13] sig=4 <__imported_wasi_snapshot_preview1_sock_listen> <- wasi_snapshot_preview1.sock_listen + - func[14] sig=5 <__imported_wasi_snapshot_preview1_sock_open> <- wasi_snapshot_preview1.sock_open + - func[15] sig=4 <__imported_wasi_snapshot_preview1_sock_addr_remote> <- wasi_snapshot_preview1.sock_addr_remote + - func[16] sig=4 <__imported_wasi_snapshot_preview1_args_get> <- wasi_snapshot_preview1.args_get + - func[17] sig=4 <__imported_wasi_snapshot_preview1_args_sizes_get> <- wasi_snapshot_preview1.args_sizes_get + ... +``` + +Developers should fill in _imports_ with enough host functions and make sure +there are no linking problems during instantiation. + +```bash +TODO: linking warnings +``` + +## A natural way + +One natural answer is "to create a list which matches every item in _the import +section_" of the .wasm. Since developers can see the section details of +a .wasm by tools like _wasm-objdump_, the answer is doable. Most of the time, +if they also prepare Wasm modules, developers have full control over import +requirements, and they only need to take a look at the order of _the import +section_. + +Yes, _the order_. A proper `wasm_extern_vec_t *imports` includes two things: + +1. how many `wasm_extern_t` +2. and order of those + +Because there is no "name information" in a `wasm_extern_t`. The only way is let +`wasm_instance_new()` to tell which item in _the import section_ of a .wasm +should match any item in `wasm_extern_vec_t *imports` is based on **_index_**. + +The algorithm is quite straightforward. The first one of _the import section_ matches +`wasm_extern_vec_t *imports->data[0] `. The second one matches `wasm_extern_vec_t *imports->data[1]`. +And so on. + +So the order of `wasm_extern_vec_t *imports` becomes quite a burden. It requires +developers always checking _the import section_ visually. + +Until here, the natural way is still workable although involving some handy work. +Right? + +## A blocker + +Sorry, the situation changes a lot when driving wasm32-wasi Wasm modules with +wasm-c-api. + +As you know, WASI provides _a set of crossing-platform standard libraries_ for +Wasm modules, and leaves some _interfaces_ for native platform-dependent supports. +Those _interfaces_ are those import items with the module name `wasi_snapshot_preview1` +in a Wasm module. + +It seems not economical to let developers provide their version of host +implementations of the `wasi_snapshot_preview1.XXX` functions. All those support +should be packed into a common library and shared in different Wasm modules. +Like a [cargo WASI](https://github.com/bytecodealliance/cargo-wasi). + +WAMR chooses to integrate the WASI support library in the runtime to reduce +developers' compilation work. It brings developers a new thing of a proper +`wasm_extern_vec_t *imports` that developers should avoid overwriting those items +of _the import section_ of a Wasm module that will be provided by the runtime. It +also not economical to code for those functions. + +Using module names as a filter seems to be a simple way. But some private +additional c/c++ libraries are supported in WAMR. Those supporting will bring +more import items that don't use `wasi_snapshot_preview1` as module names but are still +covered by the WASM runtime. Like `env.pthread_`. Plus, [the native lib registeration](https://github.com/bytecodealliance/wasm-micro-runtime/blob/main/doc/export_native_api.md) +provides another possible way to fill in the requirement of _the import section_. + +Let's take summarize. A proper `wasm_extern_vec_t *imports` should include: + +1. provides all necessary host implementations for items in _the import section_ +2. should not override runtime provided implementation or covered by native + registrations. functinal or econmical. +3. keep them in a right order + +## A recommendation + +The recommendation is: + +- use `wasm_module_imports()` to build the order +- use `wasm_importtype_is_linked()` to avoid overwriting + +[wasm-c-api-imports](.) is a simple showcase of how to do that. + +First, let's take a look at the Wasm module. [send_recv](./wasm/send_recv.c) +uses both standard WASI and WAMR_BUILD_LIB_PTHREAD supporting. Plus a private +native function `host_log`. + +So, `wasm_extern_vec_t *imports` should only include the host implementation of +`host_log` and avoid WASI related(`wasm-c-api-imports.XXX`) and pthread related(`env.pthread_XXX`). + +[Here is how to do](./host/example1.c): + +- get import types with `wasm_module_imports(0)`. it contains name information + +```c + wasm_importtype_vec_t importtypes = { 0 }; + wasm_module_imports(module, &importtypes); +``` + +- traversal import types. The final `wasm_importvec_t *imports` should have the + same order with `wasm_importtype_vec_t` + +```c + for (unsigned i = 0; i < importtypes.num_elems; i++) +``` + +- use `wasm_importtype_is_linked()` to avoid those covered by the runtime and + registered natives. A little tip is use "wasm_extern_new_empty()" to create + a placeholder. + +```c + /* use wasm_extern_new_empty() to create a placeholder */ + if (wasm_importtype_is_linked(importtype)) { + externs[i] = wasm_extern_new_empty( + store, wasm_externtype_kind(wasm_importtype_type(importtype))); + continue; + } +``` + +- use `wasm_importtype_module()` to get the module name, use `wasm_importtype_name()` + to get the field name. + +```c + const wasm_name_t *module_name = + wasm_importtype_module(importtypes.data[i]); + const wasm_name_t *field_name = + wasm_importtype_name(importtypes.data[i]); +``` + +- fill in `wasm_externvec_t *imports` dynamically and programmatically. + +```c + if (strncmp(module_name->data, "env", strlen("env")) == 0 + && strncmp(field_name->data, "log", strlen("log")) == 0) { + wasm_functype_t *log_type = wasm_functype_new_2_0( + wasm_valtype_new_i64(), wasm_valtype_new_i32()); + wasm_func_t *log_func = wasm_func_new(store, log_type, host_logs); + wasm_functype_delete(log_type); + + externs[i] = wasm_func_as_extern(log_func); + } + } +``` diff --git a/samples/wasm-c-api-imports/host/CMakeLists.txt b/samples/wasm-c-api-imports/host/CMakeLists.txt new file mode 100644 index 000000000..e2636f09e --- /dev/null +++ b/samples/wasm-c-api-imports/host/CMakeLists.txt @@ -0,0 +1,12 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 3.14) +project(host) + +set(CMAKE_BUILD_TYPE Debug) + +# +# host +add_executable(example1 ./example1.c) +target_link_libraries(example1 vmlib) diff --git a/samples/wasm-c-api-imports/host/example1.c b/samples/wasm-c-api-imports/host/example1.c new file mode 100644 index 000000000..ccf574d79 --- /dev/null +++ b/samples/wasm-c-api-imports/host/example1.c @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include "wasm_c_api.h" +#include "wasm_export.h" + +static wasm_trap_t * +host_logs(const wasm_val_vec_t *args, wasm_val_vec_t *results) +{ + return NULL; +} + +static bool +build_imports(wasm_store_t *store, const wasm_module_t *module, + wasm_extern_vec_t *out) +{ + wasm_importtype_vec_t importtypes = { 0 }; + wasm_module_imports(module, &importtypes); + + wasm_extern_t *externs[32] = { 0 }; + + for (unsigned i = 0; i < importtypes.num_elems; i++) { + wasm_importtype_t *importtype = importtypes.data[i]; + + /* use wasm_extern_new_empty() to create a placeholder */ + if (wasm_importtype_is_linked(importtype)) { + externs[i] = wasm_extern_new_empty( + store, wasm_externtype_kind(wasm_importtype_type(importtype))); + continue; + } + + const wasm_name_t *module_name = + wasm_importtype_module(importtypes.data[i]); + const wasm_name_t *field_name = + wasm_importtype_name(importtypes.data[i]); + + if (strncmp(module_name->data, "env", strlen("env")) == 0 + && strncmp(field_name->data, "log", strlen("log")) == 0) { + wasm_functype_t *log_type = wasm_functype_new_2_0( + wasm_valtype_new_i64(), wasm_valtype_new_i32()); + wasm_func_t *log_func = wasm_func_new(store, log_type, host_logs); + wasm_functype_delete(log_type); + + externs[i] = wasm_func_as_extern(log_func); + } + } + + wasm_extern_vec_new(out, importtypes.num_elems, externs); + wasm_importtype_vec_delete(&importtypes); + return true; +} + +int +main() +{ + int main_ret = EXIT_FAILURE; + + // Initialize. + printf("Initializing...\n"); + wasm_engine_t *engine = wasm_engine_new(); + if (!engine) + goto quit; + + wasm_store_t *store = wasm_store_new(engine); + if (!store) + goto delete_engine; + + // Load binary. + printf("Loading binary...\n"); +#if WASM_ENABLE_AOT != 0 && WASM_ENABLE_INTERP == 0 + FILE *file = fopen("send_recv.aot", "rb"); + printf("> Load .aot\n"); +#else + FILE *file = fopen("send_recv.wasm", "rb"); + printf("> Load .wasm\n"); +#endif + if (!file) { + printf("> Error loading module!\n"); + goto delete_store; + } + + int ret = fseek(file, 0L, SEEK_END); + if (ret == -1) { + printf("> Error loading module!\n"); + goto close_file; + } + + long file_size = ftell(file); + if (file_size == -1) { + printf("> Error loading module!\n"); + goto close_file; + } + + ret = fseek(file, 0L, SEEK_SET); + if (ret == -1) { + printf("> Error loading module!\n"); + goto close_file; + } + + wasm_byte_vec_t binary; + wasm_byte_vec_new_uninitialized(&binary, file_size); + + if (fread(binary.data, file_size, 1, file) != 1) { + printf("> Error loading module!\n"); + goto delete_binary; + } + + // Compile. + printf("Compiling module...\n"); + wasm_module_t *module = wasm_module_new(store, &binary); + if (!module) { + printf("> Error compiling module!\n"); + goto delete_binary; + } + + // Set Wasi Context + const char *addr_pool[1] = { "127.0.0.1" }; + wasm_runtime_set_wasi_addr_pool(*module, addr_pool, 1); + + // Instantiate. + printf("Instantiating module...\n"); + wasm_extern_vec_t imports = { 0 }; + ret = build_imports(store, module, &imports); + if (!ret) { + printf("> Error building imports!\n"); + goto delete_module; + } + + wasm_instance_t *instance = + wasm_instance_new(store, module, &imports, NULL); + if (!instance) { + printf("> Error instantiating module!\n"); + goto delete_imports; + } + + // Extract export. + printf("Extracting export...\n"); + wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + if (exports.size == 0) { + printf("> Error accessing exports!\n"); + goto delete_instance; + } + + /** + * should use information from wasm_module_exports to avoid hard coding "1" + */ + const wasm_func_t *start_func = wasm_extern_as_func(exports.data[1]); + if (start_func == NULL) { + printf("> Error accessing export!\n"); + goto delete_exports; + } + + // Call. "_start(nil) -> i32" + printf("Calling _start ...\n"); + wasm_val_t rs[1] = { WASM_I32_VAL(0) }; + wasm_val_vec_t args = WASM_EMPTY_VEC; + wasm_val_vec_t results = WASM_ARRAY_VEC(rs); + wasm_trap_t *trap = wasm_func_call(start_func, &args, &results); + if (trap) { + wasm_name_t message = { 0 }; + wasm_trap_message(trap, &message); + + printf("> Error calling function! %s\n", message.data); + + wasm_name_delete(&message); + wasm_trap_delete(trap); + goto delete_exports; + } + + // Print result. + printf("Printing result...\n"); + printf("> %u\n", rs[0].of.i32); + + // Shut down. + printf("Shutting down...\n"); + + // All done. + printf("Done.\n"); + main_ret = EXIT_SUCCESS; + +delete_exports: + wasm_extern_vec_delete(&exports); +delete_instance: + wasm_instance_delete(instance); +delete_imports: + wasm_extern_vec_delete(&imports); +delete_module: + wasm_module_delete(module); +delete_binary: + wasm_byte_vec_delete(&binary); +close_file: + fclose(file); +delete_store: + wasm_store_delete(store); +delete_engine: + wasm_engine_delete(engine); +quit: + return main_ret; +} \ No newline at end of file diff --git a/samples/wasm-c-api-imports/wasm/CMakeLists.txt b/samples/wasm-c-api-imports/wasm/CMakeLists.txt new file mode 100644 index 000000000..6b2743cb5 --- /dev/null +++ b/samples/wasm-c-api-imports/wasm/CMakeLists.txt @@ -0,0 +1,47 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 3.14) +project(wasm_modules) + +if(NOT SOCKET_WASI_CMAKE) + message(FATAL_ERROR "Require SOCKET_WASI_CMAKE") +endif() + +option(WASM_TO_AOT "transfer wasm to aot" OFF) +if(WASM_TO_AOT AND NOT WAMRC_PATH) + message(FATAL_ERROR "Require WAMRC_PATH when WASM_TO_AOT is ON") +endif() + +# +# c -> wasm +include(${SOCKET_WASI_CMAKE}) +add_executable(send_recv ${CMAKE_CURRENT_LIST_DIR}/send_recv.c) +set_target_properties(send_recv PROPERTIES SUFFIX .wasm) +target_include_directories(send_recv PUBLIC ${CMAKE_CURRENT_LIST_DIR}/inc) +target_link_libraries(send_recv socket_wasi_ext) +target_link_options(send_recv PRIVATE + LINKER:--export=__heap_base + LINKER:--export=__data_end + LINKER:--shared-memory,--max-memory=196608 + LINKER:--no-check-features + LINKER:--allow-undefined +) + +if(WASM_TO_AOT) + # wasm -> aot + add_custom_target(send_recv_aot ALL + COMMAND pwd && ${WAMRC_PATH} --enable-multi-thread -o ./send_recv.aot ./send_recv.wasm + DEPENDS send_recv + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) +endif() + +# +# install +if(WASM_TO_AOT) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/send_recv.aot DESTINATION . ) +else() + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/send_recv.wasm DESTINATION . ) +endif() + diff --git a/samples/wasm-c-api-imports/wasm/inc/.gitkeep b/samples/wasm-c-api-imports/wasm/inc/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/samples/wasm-c-api-imports/wasm/send_recv.c b/samples/wasm-c-api-imports/wasm/send_recv.c new file mode 100644 index 000000000..d9f95573a --- /dev/null +++ b/samples/wasm-c-api-imports/wasm/send_recv.c @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __wasi__ +#include +#include "pthread.h" +#else +#include +#endif + +static pthread_mutex_t lock = { 0 }; +static pthread_cond_t cond = { 0 }; +static bool server_create_failed = false; +static bool server_is_ready = false; + +#ifdef __wasi__ +__attribute__((import_name("log"))) extern void +host_log(uint64_t message, uint32_t length); +#endif + +static void +local_printf(const char *formatter, ...) +{ + char buffer[128] = { 0 }; + va_list args; + + va_start(args, formatter); + vsnprintf(buffer, 128, formatter, args); + va_end(args); + +#ifdef __wasi__ + host_log((uint64_t)(void *)buffer, strlen(buffer)); +#endif + printf("--> %s", buffer); +} + +void * +run_as_server(void *arg) +{ + int sock = -1, on = 1; + struct sockaddr_in addr = { 0 }; + int addrlen = 0; + int new_sock = -1; + char *buf[] = { + "The stars shine down", "It brings us light", "Light comes down", + "To make us paths", "It watches us", "And mourns for us", + }; + struct iovec iov[] = { + { .iov_base = buf[0], .iov_len = strlen(buf[0]) + 1 }, + { .iov_base = buf[1], .iov_len = strlen(buf[1]) + 1 }, + { .iov_base = buf[2], .iov_len = strlen(buf[2]) + 1 }, + { .iov_base = buf[3], .iov_len = strlen(buf[3]) + 1 }, + { .iov_base = buf[4], .iov_len = strlen(buf[4]) + 1 }, + { .iov_base = buf[5], .iov_len = strlen(buf[5]) + 1 }, + }; + struct msghdr msg = { .msg_iov = iov, .msg_iovlen = 6 }; + ssize_t send_len = 0; + + pthread_mutex_lock(&lock); + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) { + server_create_failed = true; + pthread_cond_signal(&cond); + pthread_mutex_unlock(&lock); + perror("Create a socket failed"); + return NULL; + } + +#ifndef __wasi__ + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on))) { + server_create_failed = true; + pthread_cond_signal(&cond); + pthread_mutex_unlock(&lock); + perror("Setsockopt failed"); + goto fail1; + } +#endif + + /* 0.0.0.0:1234 */ + addr.sin_family = AF_INET; + addr.sin_port = htons(1234); + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + addrlen = sizeof(addr); + if (bind(sock, (struct sockaddr *)&addr, addrlen) < 0) { + server_create_failed = true; + pthread_cond_signal(&cond); + pthread_mutex_unlock(&lock); + perror("Bind failed"); + goto fail1; + } + + if (listen(sock, 0) < 0) { + server_create_failed = true; + pthread_cond_signal(&cond); + pthread_mutex_unlock(&lock); + perror("Listen failed"); + goto fail1; + } + + server_is_ready = true; + pthread_cond_signal(&cond); + pthread_mutex_unlock(&lock); + + local_printf("Server is online ... \n"); + + new_sock = accept(sock, (struct sockaddr *)&addr, (socklen_t *)&addrlen); + if (new_sock < 0) { + perror("Accept failed"); + goto fail1; + } + + local_printf("Start sending. \n"); + send_len = sendmsg(new_sock, &msg, 0); + if (send_len < 0) { + perror("Sendmsg failed"); + goto fail2; + } + local_printf("Send %ld bytes successfully!\n", send_len); + +fail2: + close(new_sock); +fail1: + shutdown(sock, SHUT_RD); + close(sock); + return NULL; +} + +void * +run_as_client(void *arg) +{ + int sock = -1; + struct sockaddr_in addr = { 0 }; + /* buf of server is 106 bytes */ + char buf[110] = { 0 }; + struct iovec iov = { .iov_base = buf, .iov_len = sizeof(buf) }; + struct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1 }; + ssize_t recv_len = 0; + + pthread_mutex_lock(&lock); + while (!server_create_failed && !server_is_ready) { + pthread_cond_wait(&cond, &lock); + } + pthread_mutex_unlock(&lock); + + if (server_create_failed) { + return NULL; + } + + local_printf("Client is running...\n"); + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) { + perror("Create a socket failed"); + return NULL; + } + + /* 127.0.0.1:1234 */ + addr.sin_family = AF_INET; + addr.sin_port = htons(1234); + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + perror("Connect failed"); + goto fail; + } + + local_printf("Start receiving. \n"); + recv_len = recvmsg(sock, &msg, 0); + if (recv_len < 0) { + perror("Recvmsg failed"); + goto fail; + } + + local_printf("Receive %ld bytes successlly!\n", recv_len); + assert(recv_len == 106); + + local_printf("Data:\n"); + char *s = msg.msg_iov->iov_base; + while (strlen(s) > 0) { + local_printf(" %s\n", s); + s += strlen(s) + 1; + } + +fail: + shutdown(sock, SHUT_RD); + close(sock); + return NULL; +} + +int +main(int argc, char *argv[]) +{ + pthread_t cs[2] = { 0 }; + uint8_t i = 0; + int ret = EXIT_SUCCESS; + + if (pthread_mutex_init(&lock, NULL)) { + perror("Initialize mutex failed"); + ret = EXIT_FAILURE; + goto RETURN; + } + + if (pthread_cond_init(&cond, NULL)) { + perror("Initialize condition failed"); + ret = EXIT_FAILURE; + goto DESTROY_MUTEX; + } + + if (pthread_create(&cs[0], NULL, run_as_server, NULL)) { + perror("Create a server thread failed"); + ret = EXIT_FAILURE; + goto DESTROY_COND; + } + + if (pthread_create(&cs[1], NULL, run_as_client, NULL)) { + perror("Create a client thread failed"); + ret = EXIT_FAILURE; + goto DESTROY_COND; + } + + for (i = 0; i < 2; i++) { + pthread_join(cs[i], NULL); + } + +DESTROY_COND: + pthread_cond_destroy(&cond); +DESTROY_MUTEX: + pthread_mutex_destroy(&lock); +RETURN: + return ret; +} diff --git a/samples/wasm-c-api/CMakeLists.txt b/samples/wasm-c-api/CMakeLists.txt index a64946240..e2be3b8f4 100644 --- a/samples/wasm-c-api/CMakeLists.txt +++ b/samples/wasm-c-api/CMakeLists.txt @@ -136,26 +136,13 @@ include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) set(MM_UTIL src/utils/multi_module_utils.c) # build executable for each .c -set(EXAMPLES - callback - callback_chain - clone - empty_imports - global - hello - hostref - memory - reflect - table - threads - trap -) - -if(WAMR_BUILD_JIT AND WAMR_BUILD_LAZY_JIT) - if((${WAMR_BUILD_JIT} EQUAL 1) AND (${WAMR_BUILD_LAZY_JIT} EQUAL 1)) - list(APPEND EXAMPLES serialize) - endif() -endif() +list(APPEND EXAMPLES callback callback_chain empty_imports global hello hostref memory reflect table trap) +# FIXME enable both in the future +#list(APPEND EXAMPLES clone threads) +# FIXME +# if(WAMR_BUILD_JIT EQUAL 1 AND WAMR_BUILD_LAZY_JIT EQUAL 0) +# list(APPEND EXAMPLES serialize) +# endif() check_pie_supported() diff --git a/samples/wasm-c-api/README.md b/samples/wasm-c-api/README.md index 51b8642a2..b23276706 100644 --- a/samples/wasm-c-api/README.md +++ b/samples/wasm-c-api/README.md @@ -4,9 +4,9 @@ Before staring, we need to download and intall [WABT](https://github.com/WebAsse ``` shell $ cd /opt -$ wget https://github.com/WebAssembly/wabt/releases/download/1.0.19/wabt-1.0.19-ubuntu.tar.gz -$ tar -xzf wabt-1.0.19-ubuntu.tar.gz -$ mv wabt-1.0.19 wabt +$ wget https://github.com/WebAssembly/wabt/releases/download/1.0.31/wabt-1.0.31-ubuntu.tar.gz +$ tar -xzf wabt-1.0.31-ubuntu.tar.gz +$ mv wabt-1.0.31 wabt ``` By default, all samples are compiled and run in "interpreter" mode. @@ -47,4 +47,4 @@ $ ./global $ ... $ ./callback $ ... -``` \ No newline at end of file +``` diff --git a/samples/workload/CMakeLists.txt b/samples/workload/CMakeLists.txt new file mode 100644 index 000000000..667f0b4e8 --- /dev/null +++ b/samples/workload/CMakeLists.txt @@ -0,0 +1,116 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 3.14) + +project(wasm_workloads) + +####################################### +add_subdirectory(bwa) +add_subdirectory(meshoptimizer) +add_subdirectory(wasm-av1) + +####################################### +include(ExternalProject) + +################ iwasm ################ +ExternalProject_Add(iwasm + PREFIX + iwasm-build + BUILD_ALWAYS + YES + SOURCE_DIR + ${CMAKE_CURRENT_SOURCE_DIR}/../../product-mini/platforms/linux + CONFIGURE_COMMAND + ${CMAKE_COMMAND} -S ${CMAKE_CURRENT_SOURCE_DIR}/../../product-mini/platforms/linux -B build -DWAMR_BUILD_LIBC_EMCC=1 + BUILD_COMMAND + ${CMAKE_COMMAND} --build build --parallel 4 + INSTALL_COMMAND + # FIXME: replace with --install + ${CMAKE_COMMAND} -E copy_if_different + ${CMAKE_CURRENT_BINARY_DIR}/iwasm-build/src/iwasm-build/build/iwasm + ${CMAKE_CURRENT_BINARY_DIR}/iwasm +) + +################ wamrc ################ +ExternalProject_Add(wamrc + PREFIX + wamrc-build + BUILD_ALWAYS + YES + SOURCE_DIR + ${CMAKE_CURRENT_SOURCE_DIR}/../../wamr-compiler + CONFIGURE_COMMAND + ${CMAKE_COMMAND} -S ${CMAKE_CURRENT_SOURCE_DIR}/../../wamr-compiler -B build + BUILD_COMMAND + ${CMAKE_COMMAND} --build build --parallel 4 + INSTALL_COMMAND + # FIXME: replace with --install + ${CMAKE_COMMAND} -E copy_if_different + ${CMAKE_CURRENT_BINARY_DIR}/wamrc-build/src/wamrc-build/build/wamrc + ${CMAKE_CURRENT_BINARY_DIR}/wamrc +) + +################ .aot ################ +add_custom_target( + bwa_to_aot + ALL + DEPENDS + bwa wamrc + COMMAND + ./wamrc -o bwa.aot ./bwa/bwa.wasm + WORKING_DIRECTORY + ${CMAKE_CURRENT_BINARY_DIR} +) + +add_custom_target( + codecbench_to_aot + ALL + DEPENDS + codecbench wamrc + COMMAND + ./wamrc -o codecbench.aot ./meshoptimizer/codecbench.wasm + WORKING_DIRECTORY + ${CMAKE_CURRENT_BINARY_DIR} +) + +add_custom_target( + av1_to_aot + ALL + DEPENDS + av1 wamrc + COMMAND + ./wamrc -o testavx.aot ./wasm-av1/testavx.opt.wasm + WORKING_DIRECTORY + ${CMAKE_CURRENT_BINARY_DIR} +) + +################ smoking test ################ +include(CTest) + +add_test( + NAME + run_bwa + COMMAND + ./iwasm --dir=. ./bwa.aot index ./bwa/hs38DH-extra.fa + WORKING_DIRECTORY + ${CMAKE_CURRENT_BINARY_DIR} +) + +add_test( + NAME + run_codecbench + COMMAND + ./iwasm codecbench.aot + WORKING_DIRECTORY + ${CMAKE_CURRENT_BINARY_DIR} +) + +add_test( + NAME + run_av1 + COMMAND + ./iwasm --dir=. testavx.aot ./wasm-av1/elephants_dream_480p24.ivf + WORKING_DIRECTORY + ${CMAKE_CURRENT_BINARY_DIR} +) diff --git a/samples/workload/README.md b/samples/workload/README.md index dd94e05ca..0e6a3a41b 100644 --- a/samples/workload/README.md +++ b/samples/workload/README.md @@ -1,41 +1,30 @@ -All workloads have similar requirment of software dependencies, including -**emsdk**, **wabt** and **binaryen** +All workloads have similar requirment of software dependencies, including **emsdk** and **binaryen** -> There might be slight differences when using MacOS and other Linux distro than Ubuntu. This document only target -Ubuntu 18.04 as example. +> There might be slight differences when using MacOS and other Linux distro than Ubuntu. This document targets +Ubuntu 20.04 as an example. ## Installation instructions -use [preparation.sh](./preparation.sh) to install all dependencies before compiling any workload. +use [preparation.sh](./preparation.sh) to install all dependencies before compiling any workload. Or use [*vscode DevContainer*](../../.devcontainer/) -for details, the script includes below steps: - -- **wabt**. Install - [latest release](https://github.com/WebAssembly/wabt/releases/download/1.0.23/wabt-1.0.23-ubuntu.tar.gz) - to */opt/wabt* - -``` bash -$ wget https://github.com/WebAssembly/wabt/releases/download/${WABT_VER}/${WABT_FILE} -$ tar zxf ${WABT_FILE} -C /opt -$ ln -sf /opt/wabt-${WABT_VER} /opt/wabt -``` +The script installs below software: - **emsdk**. Refer to [the guide](https://emscripten.org/docs/getting_started/downloads.html). Don't forget to activate emsdk and set up environment variables. Verify it with `echo ${EMSDK}`. Please be sure to install and activate the building - of 2.0.26 + of 3.0.0 ``` bash $ cd /opt $ git clone https://github.com/emscripten-core/emsdk.git $ cd emsdk $ git pull -$ ./emsdk install 2.0.26 -$ ./emsdk activate 2.0.26 +$ ./emsdk install 3.0.0 +$ ./emsdk activate 3.0.0 $ echo "source /opt/emsdk/emsdk_env.sh" >> "${HOME}"/.bashrc ``` - **binaryen**. Install - [latest release](https://github.com/WebAssembly/binaryen/releases/download/version_101/binaryen-version_101-x86_64-linux.tar.gz) + [latest release](https://github.com/WebAssembly/binaryen/releases/download/version_111/binaryen-version_111-x86_64-linux.tar.gz) to */opt/binaryen* ``` bash diff --git a/samples/workload/XNNPACK/CMakeLists.txt b/samples/workload/XNNPACK/CMakeLists.txt index 3f67367a7..532544f9c 100644 --- a/samples/workload/XNNPACK/CMakeLists.txt +++ b/samples/workload/XNNPACK/CMakeLists.txt @@ -15,8 +15,9 @@ ExternalProject_Add(xnnpack GIT_PROGRESS ON SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/xnnpack UPDATE_COMMAND git checkout . - && git reset --hard 4d738aef36872669e4bba05a4b259149ba8e62e1 - && cmake -E copy ${CMAKE_CURRENT_SOURCE_DIR}/benchmark.patch ${CMAKE_CURRENT_SOURCE_DIR}/xnnpack/third_party + && git reset --hard 4570a7151aa4f3e57eca14a575eeff6bb13e26be + && cmake -E copy ${CMAKE_CURRENT_SOURCE_DIR}/xnnpack/google3/third_party/XNNPACK/microkernels.bzl + ${CMAKE_CURRENT_SOURCE_DIR}/xnnpack/ && git apply ${CMAKE_CURRENT_SOURCE_DIR}/xnnpack.patch CONFIGURE_COMMAND "" # grep xnnpack_benchmark -A 1 BUILD.bazel \ @@ -24,70 +25,123 @@ ExternalProject_Add(xnnpack # | awk '{print $3}' \ # | sed -e 's/\"//g' -e 's/,//g' -e 's/^/\/\/:/g' BUILD_COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR}/xnnpack - && bazel --output_user_root=build_user_output build -c opt --config=wasm + && bazel --output_user_root=build-user-output build -c opt --config=wasm //:qs8_dwconv_bench.wasm + //:qs8_f32_vcvt_bench.wasm //:qs8_gemm_bench.wasm //:qs8_requantization_bench.wasm //:qs8_vadd_bench.wasm //:qs8_vaddc_bench.wasm + //:qs8_vcvt_bench.wasm + //:qs8_vlrelu_bench.wasm + //:qs8_vmul_bench.wasm + //:qs8_vmulc_bench.wasm + //:qu8_f32_vcvt_bench.wasm //:qu8_gemm_bench.wasm //:qu8_requantization_bench.wasm //:qu8_vadd_bench.wasm //:qu8_vaddc_bench.wasm + //:qu8_vcvt_bench.wasm + //:qu8_vlrelu_bench.wasm + //:qu8_vmul_bench.wasm + //:qu8_vmulc_bench.wasm + //:bf16_gemm_bench.wasm //:f16_igemm_bench.wasm //:f16_gemm_bench.wasm + //:f16_raddstoreexpminusmax_bench.wasm //:f16_spmm_bench.wasm - //:f16_vrelu_bench.wasm + //:f16_vsigmoid_bench.wasm + //:f16_f32_vcvt_bench.wasm //:f32_igemm_bench.wasm //:f32_conv_hwc_bench.wasm + //:f16_conv_hwc2chw_bench.wasm + //:f16_gavgpool_cw_bench.wasm + //:f32_gavgpool_cw_bench.wasm //:f32_conv_hwc2chw_bench.wasm //:f16_dwconv_bench.wasm //:f32_dwconv_bench.wasm //:f32_dwconv2d_chw_bench.wasm + //:f16_dwconv2d_chw_bench.wasm + //:f32_f16_vcvt_bench.wasm + //:xx_transpose_bench.wasm + //:x8_transpose_bench.wasm + //:x16_transpose_bench.wasm + //:x24_transpose_bench.wasm + //:x32_transpose_bench.wasm + //:x64_transpose_bench.wasm //:f32_gemm_bench.wasm + //:f32_qs8_vcvt_bench.wasm + //:f32_qu8_vcvt_bench.wasm //:f32_raddexpminusmax_bench.wasm //:f32_raddextexp_bench.wasm //:f32_raddstoreexpminusmax_bench.wasm //:f32_rmax_bench.wasm //:f32_spmm_bench.wasm //:f32_softmax_bench.wasm + //:f16_velu_bench.wasm //:f32_velu_bench.wasm //:f32_vhswish_bench.wasm + //:f32_vlrelu_bench.wasm //:f32_vrelu_bench.wasm //:f32_vscaleexpminusmax_bench.wasm //:f32_vscaleextexp_bench.wasm //:f32_vsigmoid_bench.wasm + //:f16_vsqrt_bench.wasm //:f32_vsqrt_bench.wasm //:f32_im2col_gemm_bench.wasm //:rounding_bench.wasm + //:s16_rmaxabs_bench.wasm + //:s16_window_bench.wasm + //:u32_filterbank_accumulate_bench.wasm + //:u32_filterbank_subtract_bench.wasm + //:u32_vlog_bench.wasm + //:u64_u32_vsqrtshift_bench.wasm + //:i16_vlshift_bench.wasm + //:cs16_vsquareabs_bench.wasm + //:cs16_bfly4_bench.wasm + //:cs16_fftr_bench.wasm + //:x8_lut_bench.wasm + //:abs_bench.wasm //:average_pooling_bench.wasm //:bankers_rounding_bench.wasm //:ceiling_bench.wasm //:channel_shuffle_bench.wasm + //:convert_bench.wasm //:convolution_bench.wasm //:deconvolution_bench.wasm //:elu_bench.wasm //:floor_bench.wasm //:global_average_pooling_bench.wasm //:hardswish_bench.wasm + //:leaky_relu_bench.wasm //:max_pooling_bench.wasm + //:negate_bench.wasm //:sigmoid_bench.wasm //:prelu_bench.wasm //:softmax_bench.wasm + //:square_bench.wasm //:square_root_bench.wasm //:truncation_bench.wasm + //:f16_gemm_e2e_bench.wasm //:f32_dwconv_e2e_bench.wasm //:f32_gemm_e2e_bench.wasm //:qs8_dwconv_e2e_bench.wasm //:qs8_gemm_e2e_bench.wasm + //:qu8_gemm_e2e_bench.wasm //:qu8_dwconv_e2e_bench.wasm //:end2end_bench.wasm + //:f16_exp_ulp_eval.wasm + //:f16_expminus_ulp_eval.wasm + //:f16_expm1minus_ulp_eval.wasm + //:f16_sigmoid_ulp_eval.wasm + //:f16_sqrt_ulp_eval.wasm //:f32_exp_ulp_eval.wasm //:f32_expminus_ulp_eval.wasm //:f32_expm1minus_ulp_eval.wasm //:f32_extexp_ulp_eval.wasm //:f32_sigmoid_ulp_eval.wasm //:f32_sqrt_ulp_eval.wasm + //:f32_tanh_ulp_eval.wasm INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/xnnpack/bazel-out/wasm-opt/bin/ ${CMAKE_BINARY_DIR}/wasm-opt diff --git a/samples/workload/XNNPACK/README.md b/samples/workload/XNNPACK/README.md index f6a207293..7984d9cee 100644 --- a/samples/workload/XNNPACK/README.md +++ b/samples/workload/XNNPACK/README.md @@ -24,7 +24,7 @@ Firstly please build iwasm with simd, libc-emcc and lib-pthread support: ``` bash $ cd /product-mini/platforms/linux/ $ mkdir build && cd build -$ cmake .. -DWAMR_BUILD_SIMD=1 -DWAMR_BUILD_LIBC_EMCC=1 -DWAMR_BUILD_LIB_PTHREAD=1 +$ cmake .. -DWAMR_BUILD_LIBC_EMCC=1 -DWAMR_BUILD_LIB_PTHREAD=1 $ make ``` @@ -42,7 +42,7 @@ Then compile wasm file to aot file and run: ``` shell $ cd /samples/workload/XNNPACK/xnnpack/bazel-bin -$ wamrc --enable-simd -o average_pooling_bench.aot average_pooling_bench.wasm (or other wasm files) +$ wamrc -o average_pooling_bench.aot average_pooling_bench.wasm (or other wasm files) $ iwasm average_pooling_bench.aot ``` diff --git a/samples/workload/XNNPACK/build_workload.sh b/samples/workload/XNNPACK/build_workload.sh deleted file mode 120000 index a31afa928..000000000 --- a/samples/workload/XNNPACK/build_workload.sh +++ /dev/null @@ -1 +0,0 @@ -../docker/build_workload.sh \ No newline at end of file diff --git a/samples/workload/XNNPACK/xnnpack.patch b/samples/workload/XNNPACK/xnnpack.patch index 7eed678b6..f7d0a01d4 100644 --- a/samples/workload/XNNPACK/xnnpack.patch +++ b/samples/workload/XNNPACK/xnnpack.patch @@ -1,8 +1,8 @@ diff --git a/.bazelrc b/.bazelrc -index ec740f38..29f9d56e 100644 +index 688279da1..376996885 100644 --- a/.bazelrc +++ b/.bazelrc -@@ -49,4 +49,9 @@ build:ios_fat --watchos_cpus=armv7k +@@ -53,4 +53,9 @@ build:ios_fat --watchos_cpus=armv7k build:macos --apple_platform_type=macos build:macos_arm64 --config=macos @@ -11,42 +11,26 @@ index ec740f38..29f9d56e 100644 +build:macos_arm64 --cpu=darwin_arm64 + +build:wasm --cpu=wasm -+build:wasm --copt=-msimd128 ++build:wasm --features=wasm_simd +build:wasm --crosstool_top=@emsdk//emscripten_toolchain:everything +build:wasm --host_crosstool_top=@bazel_tools//tools/cpp:toolchain -diff --git a/BUILD.bazel b/BUILD.bazel -index 3fc8139f..c893356d 100644 ---- a/BUILD.bazel -+++ b/BUILD.bazel -@@ -11988,7 +11988,6 @@ config_setting( - values = { - "crosstool_top": "@emsdk//emscripten_toolchain:everything", - "cpu": "wasm", -- "copt": "-msimd128", - "copt": "-mrelaxed-simd", - }, - ) diff --git a/WORKSPACE b/WORKSPACE -index c58e76b6..30934678 100644 +index cd8960ffa..5d3e685f4 100644 --- a/WORKSPACE +++ b/WORKSPACE -@@ -21,6 +21,7 @@ http_archive( - name = "com_google_benchmark", - strip_prefix = "benchmark-master", - urls = ["https://github.com/google/benchmark/archive/master.zip"], -+ patches = ["@//third_party:benchmark.patch"], - ) - - # FP16 library, used for half-precision conversions -@@ -84,6 +85,19 @@ http_archive( - ], +@@ -92,8 +92,25 @@ http_archive( + ], ) ++load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +http_archive( -+ name = "emsdk", -+ strip_prefix = "emsdk-2.0.26/bazel", -+ url = "https://github.com/emscripten-core/emsdk/archive/refs/tags/2.0.26.tar.gz", -+ sha256 = "79e7166aa8eaae6e52cef1363b2d8db795d03684846066bc51f9dcf905dd58ad", ++ name = "emsdk", ++ # Use emsdk-3.0.0 since the larger version may: ++ # - compress the wasm file into a tar file but not directly generate wasm file ++ # - generate incomplete implementation of libc API, e.g. throw exception in getentropy ++ strip_prefix = "emsdk-3.0.0/bazel", ++ url = "https://github.com/emscripten-core/emsdk/archive/refs/tags/3.0.0.tar.gz", ++ sha256 = "a41dccfd15be9e85f923efaa0ac21943cbab77ec8d39e52f25eca1ec61a9ac9e" +) + +load("@emsdk//:deps.bzl", emsdk_deps = "deps") @@ -56,13 +40,17 @@ index c58e76b6..30934678 100644 +emsdk_emscripten_deps() + # Android NDK location and version is auto-detected from $ANDROID_NDK_HOME environment variable - android_ndk_repository(name = "androidndk") +-android_ndk_repository(name = "androidndk") ++#android_ndk_repository(name = "androidndk") + # Android SDK location and API is auto-detected from $ANDROID_HOME environment variable +-android_sdk_repository(name = "androidsdk") ++#android_sdk_repository(name = "androidsdk") diff --git a/build_defs.bzl b/build_defs.bzl -index fbadb400..e496b78d 100644 +index b8217a18d..da232966e 100644 --- a/build_defs.bzl +++ b/build_defs.bzl -@@ -430,7 +430,7 @@ def xnnpack_benchmark(name, srcs, copts = [], deps = [], tags = []): +@@ -380,7 +380,7 @@ def xnnpack_benchmark(name, srcs, copts = [], deps = [], tags = []): explicitly specified. """ native.cc_binary( @@ -72,7 +60,7 @@ index fbadb400..e496b78d 100644 copts = xnnpack_std_cxxopts() + [ "-Iinclude", diff --git a/emscripten.bzl b/emscripten.bzl -index 130d5f16..2696ad54 100644 +index f1557a7b1..7f964a094 100644 --- a/emscripten.bzl +++ b/emscripten.bzl @@ -25,12 +25,19 @@ def xnnpack_emscripten_benchmark_linkopts(): @@ -84,7 +72,7 @@ index 130d5f16..2696ad54 100644 - "-s EXIT_RUNTIME=1", + "-s ERROR_ON_UNDEFINED_SYMBOLS=0", "-s ALLOW_MEMORY_GROWTH=1", - "-s TOTAL_MEMORY=445644800", # 425M + "-s TOTAL_MEMORY=536870912", # 512M - "--pre-js $(location :preamble.js.lds)", + "-s USE_PTHREADS=0", + "-s STANDALONE_WASM=1", @@ -99,11 +87,33 @@ index 130d5f16..2696ad54 100644 ] def xnnpack_emscripten_deps(): +diff --git a/src/log.c b/src/log.c +index 5715f2f85..4b3e4261b 100644 +--- a/src/log.c ++++ b/src/log.c +@@ -55,7 +55,7 @@ + #endif + + #if XNN_LOG_TO_STDIO +-static void xnn_vlog(int output_handle, const char* prefix, size_t prefix_length, const char* format, va_list args) { ++void xnn_vlog(int output_handle, const char* prefix, size_t prefix_length, const char* format, va_list args) { + char stack_buffer[XNN_LOG_STACK_BUFFER_SIZE]; + char* heap_buffer = NULL; + char* out_buffer = &stack_buffer[0]; diff --git a/third_party/cpuinfo.BUILD b/third_party/cpuinfo.BUILD -index 128d683e..f6c287c4 100644 +index 1997f4e3a..5e03c43af 100644 --- a/third_party/cpuinfo.BUILD +++ b/third_party/cpuinfo.BUILD -@@ -343,5 +343,5 @@ config_setting( +@@ -150,7 +150,7 @@ cc_library( + "src/arm/midr.h", + ], + deps = [ +- "@clog", ++ "//deps/clog" + ], + ) + +@@ -352,5 +352,5 @@ config_setting( config_setting( name = "emscripten", diff --git a/samples/workload/bwa/CMakeLists.bwa_wasm.txt b/samples/workload/bwa/CMakeLists.bwa_wasm.txt index c68b942f1..a8d1d8821 100644 --- a/samples/workload/bwa/CMakeLists.bwa_wasm.txt +++ b/samples/workload/bwa/CMakeLists.bwa_wasm.txt @@ -1,11 +1,14 @@ # Copyright (C) 2019 Intel Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -cmake_minimum_required (VERSION 3.0) +cmake_minimum_required (VERSION 3.14) project(bwa_wasm C) -include(${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/preparation.cmake) +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../cmake) + +################ dependencies ################ +find_package(Binaryen 111 REQUIRED) ################ LIBZ ################ set(LIBZ_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../libz) @@ -86,12 +89,6 @@ add_executable(${PROJECT_NAME} ${BWA_SOURCE}) set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME bwa.wasm) -target_include_directories(${PROJECT_NAME} - PRIVATE - ${WASI_SDK_HOME}/share/wasi-sysroot/include/libc/musl - ${WASI_SDK_HOME}/share/wasi-sysroot/include/sse -) - target_compile_definitions(${PROJECT_NAME} PRIVATE USE_MALLOC_WRAPPERS @@ -117,7 +114,7 @@ target_link_libraries(${PROJECT_NAME} z_wasm wasi-emulated-process-clocks) add_custom_target(bwa_wasm_opt ALL COMMAND - ${WASM_OPT} -Oz --enable-simd -o bwa.opt.wasm bwa.wasm + ${Binaryen_WASM_OPT} -Oz --enable-simd -o bwa.opt.wasm bwa.wasm BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/bwa.opt.wasm WORKING_DIRECTORY diff --git a/samples/workload/bwa/CMakeLists.txt b/samples/workload/bwa/CMakeLists.txt index 7f7e31825..5db52a38b 100644 --- a/samples/workload/bwa/CMakeLists.txt +++ b/samples/workload/bwa/CMakeLists.txt @@ -1,11 +1,19 @@ # Copyright (C) 2019 Intel Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -cmake_minimum_required (VERSION 2.8...3.16) +cmake_minimum_required (VERSION 3.14) project(bwa_wasm) -include(${CMAKE_CURRENT_SOURCE_DIR}/../cmake/preparation.cmake) +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../cmake) + +################ dependencies ################ +find_package(Python3 REQUIRED) +find_package(WASISDK 16.0 REQUIRED) +execute_process( + COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_LIST_DIR}/../../../test-tools/pick-up-emscripten-headers/collect_files.py --install ../include --loglevel=ERROR + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} +) ####################################### include(ExternalProject) @@ -13,7 +21,7 @@ include(ExternalProject) ################ libz ################ ExternalProject_Add(libz_src GIT_REPOSITORY https://github.com/madler/zlib.git - GIT_TAG master + GIT_TAG 04f42ceca40f73e2978b50e93806c2a18c1281fc GIT_PROGRESS ON GIT_SHALLOW ON SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libz @@ -27,7 +35,7 @@ ExternalProject_Add(libz_src ################ bwa ################ ExternalProject_Add(bwa GIT_REPOSITORY https://github.com/lh3/bwa.git - GIT_TAG master + GIT_TAG 139f68fc4c3747813783a488aef2adc86626b01b GIT_PROGRESS ON GIT_SHALLOW ON SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/bwa @@ -37,10 +45,29 @@ ExternalProject_Add(bwa && ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.bwa_wasm.txt CMakeLists.txt && git apply ../bwa.patch CONFIGURE_COMMAND ${CMAKE_COMMAND} - -DWASI_SDK_PREFIX=${WASI_SDK_HOME} - -DCMAKE_TOOLCHAIN_FILE=${WASI_SDK_HOME}/share/cmake/wasi-sdk.cmake - -DCMAKE_SYSROOT=${WASI_SDK_HOME}/share/wasi-sysroot + -DWASI_SDK_PREFIX=${WASISDK_HOME} + -DCMAKE_TOOLCHAIN_FILE=${WASISDK_TOOLCHAIN} + -DCMAKE_SYSROOT=${WASISDK_SYSROOT} + -DCMAKE_C_FLAGS=-isystem\ ${CMAKE_CURRENT_SOURCE_DIR}/../include/sse\ -isystem\ ${CMAKE_CURRENT_SOURCE_DIR}/../include/libc/musl ${CMAKE_CURRENT_SOURCE_DIR}/bwa - BUILD_COMMAND make bwa_wasm_opt - INSTALL_COMMAND ${CMAKE_COMMAND} -E copy ./bwa.opt.wasm ${CMAKE_BINARY_DIR}/bwa.wasm + BUILD_COMMAND make bwa_wasm_opt -j 4 + INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_if_different ./bwa.opt.wasm ${CMAKE_CURRENT_BINARY_DIR}/bwa.wasm +) + +################ bwa data ################ +ExternalProject_Add(bwa-kit + PREFIX bwa-kit + URL https://sourceforge.net/projects/bio-bwa/files/bwakit/bwakit-0.7.15_x64-linux.tar.bz2/download + URL_HASH SHA256=0a7b11971bc7916b68e9df35a364afe77cb3000df02ffb3a6fbd1aff9be5878c + DOWNLOAD_NAME bwakit-0.7.15_x64-linux.tar.bz2 + DOWNLOAD_EXTRACT_TIMESTAMP ON + DOWNLOAD_NO_EXTRACT OFF + DOWNLOAD_NO_PROGRESS ON + UPDATE_COMMAND "" + PATCH_COMMAND "" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${CMAKE_CURRENT_BINARY_DIR}/bwa-kit/src/bwa-kit/resource-GRCh38/hs38DH-extra.fa + ${CMAKE_CURRENT_BINARY_DIR}/hs38DH-extra.fa ) diff --git a/samples/workload/bwa/README.md b/samples/workload/bwa/README.md index e826bb61f..a8fbe3e69 100644 --- a/samples/workload/bwa/README.md +++ b/samples/workload/bwa/README.md @@ -33,7 +33,7 @@ Firstly please build iwasm with simd support: ``` shell $ cd /product-mini/platforms/linux/ $ mkdir build && cd build -$ cmake .. -DWAMR_BUILD_SIMD=1 +$ cmake .. $ make ``` @@ -41,6 +41,6 @@ Then compile wasm file to aot file and run: ``` shell $ cd /samples/workload/bwa/build -$ /wamr-compiler/build/wamrc --enable-simd -o bwa.aot bwa.wasm +$ /wamr-compiler/build/wamrc -o bwa.aot bwa.wasm $ /product-mini/platforms/linux/iwasm --dir=. bwa.aot index hs38DH.fa ``` diff --git a/samples/workload/bwa/build_workload.sh b/samples/workload/bwa/build_workload.sh deleted file mode 120000 index a31afa928..000000000 --- a/samples/workload/bwa/build_workload.sh +++ /dev/null @@ -1 +0,0 @@ -../docker/build_workload.sh \ No newline at end of file diff --git a/samples/workload/cmake/FindBinaryen.cmake b/samples/workload/cmake/FindBinaryen.cmake new file mode 100644 index 000000000..b4a647861 --- /dev/null +++ b/samples/workload/cmake/FindBinaryen.cmake @@ -0,0 +1,43 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# +# Output below variables: +# - Binaryen_HOME. the installation location +# + +include(CMakePrintHelpers) +include(FindPackageHandleStandardArgs) + +file(GLOB Binaryen_SEARCH_PATH "/opt/binaryen*") +find_path(Binaryen_HOME + NAMES bin/wasm-opt + PATHS ${Binaryen_SEARCH_PATH} + NO_CMAKE_FIND_ROOT_PATH + NO_SYSTEM_ENVIRONMENT_PATH + REQUIRED +) + +execute_process( + COMMAND ${Binaryen_HOME}/bin/wasm-opt --version + OUTPUT_VARIABLE WASM_OPT_OUTPUT + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +string(REGEX MATCH version_[0-9]+ Binaryen_VERSION_tmp ${WASM_OPT_OUTPUT}) +string(REGEX MATCH [0-9]+ Binaryen_VERSION ${Binaryen_VERSION_tmp}) + +#cmake_print_variables(Binaryen_VERSION_tmp Binaryen_VERSION) + +find_package_handle_standard_args(Binaryen REQUIRED_VARS Binaryen_HOME VERSION_VAR Binaryen_VERSION) + +if(Binaryen_FOUND) + mark_as_advanced(Binaryen_SEARCH_PATH) + mark_as_advanced(Binaryen_VERSION_tmp) + mark_as_advanced(Binaryen_VERSION) + mark_as_advanced(WASM_OPT_OUTPUT) + + set(Binaryen_WASM_OPT ${Binaryen_HOME}/bin/wasm-opt) +else() + # TODO: install WASISDK +endif() diff --git a/samples/workload/cmake/FindWASISDK.cmake b/samples/workload/cmake/FindWASISDK.cmake new file mode 100644 index 000000000..fff8aea4a --- /dev/null +++ b/samples/workload/cmake/FindWASISDK.cmake @@ -0,0 +1,38 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# +# Output below variables: +# - WASISDK_HOME. the installation location +# - WASISDK_SYSROOT. where wasi-sysroot is +# - WASISDK_TOOLCHAIN. where wasi-sdk.cmake is +# + +include(CMakePrintHelpers) +include(FindPackageHandleStandardArgs) + +file(GLOB WASISDK_SEARCH_PATH "/opt/wasi-sdk-*") +find_path(WASISDK_HOME + NAMES share/wasi-sysroot + PATHS ${WASISDK_SEARCH_PATH} + NO_CMAKE_FIND_ROOT_PATH + NO_SYSTEM_ENVIRONMENT_PATH + REQUIRED +) + +string(REGEX MATCH [0-9]+\.[0-9]+\.*[0-9]* WASISDK_VERSION ${WASISDK_HOME}) + +#cmake_print_variables(WASISDK_HOME WASISDK_VERSION) +find_package_handle_standard_args(WASISDK REQUIRED_VARS WASISDK_HOME VERSION_VAR WASISDK_VERSION) + +if(WASISDK_FOUND) + mark_as_advanced(WASISDK_SEARCH_PATH) + mark_as_advanced(WASISDK_VERSION) + + set(WASISDK_CC_COMMAND ${WASISDK_HOME}/bin/clang) + set(WASISDK_CXX_COMMAND ${WASISDK_HOME}/bin/clang++) + set(WASISDK_SYSROOT ${WASISDK_HOME}/share/wasi-sysroot) + set(WASISDK_TOOLCHAIN ${WASISDK_HOME}/share/cmake/wasi-sdk.cmake) +else() + # TODO: install WASISDK +endif() diff --git a/samples/workload/cmake/preparation.cmake b/samples/workload/cmake/preparation.cmake deleted file mode 100644 index 326943c83..000000000 --- a/samples/workload/cmake/preparation.cmake +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright (C) 2019 Intel Corporation. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -####################################### -include(ExternalProject) - -file(REAL_PATH ../../.. WAMR_ROOT - BASE_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} -) - -find_path(WASI_SDK_PARENT - name wasi-sdk - PATHS ${WAMR_ROOT}/test-tools/ - NO_DEFAULT_PATH - NO_CMAKE_FIND_ROOT_PATH -) - -if(NOT WASI_SDK_PARENT) - message(FATAL_ERROR - "can not find 'wasi-sdk' under ${WAMR_ROOT}/test-tools, " - "please run ${WAMR_ROOT}/test-tools/build-wasi-sdk/build_wasi_sdk.py " - "to build wasi-sdk and try again" - ) -endif() - -set(WASI_SDK_HOME ${WASI_SDK_PARENT}/wasi-sdk) -message(CHECK_START "Detecting WASI-SDK at ${WASI_SDK_HOME}") -if(EXISTS "${WASI_SDK_HOME}/share/cmake/wasi-sdk.cmake") - message(CHECK_PASS "found") -else() - message(CHECK_FAIL "not found") -endif() - -################ BINARYEN ################ -find_program(WASM_OPT - NAMES wasm-opt - PATHS /opt/binaryen-version_101/bin /opt/binaryen/bin - NO_DEFAULT_PATH - NO_CMAKE_FIND_ROOT_PATH -) - -if(NOT WASM_OPT) - message(FATAL_ERROR - "can not find wasm-opt. " - "please download it from " - "https://github.com/WebAssembly/binaryen/releases/download/version_101/binaryen-version_101-x86_64-linux.tar.gz " - "and install it under /opt" - ) -endif() diff --git a/samples/workload/docker/build_workload.sh b/samples/workload/docker/build_workload.sh deleted file mode 100755 index 640cca97f..000000000 --- a/samples/workload/docker/build_workload.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env bash -# -# Copyright (C) 2019 Intel Corporation. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -# - -readonly SCRIPT_PATH=$(dirname "$(realpath "$0")") -readonly ROOT=$(realpath "${SCRIPT_PATH}"/../../../) -readonly CURRENT_PATH=$(pwd) -readonly CURRENT_RELATIVE_ROOT=$(realpath --relative-base ${ROOT} ${CURRENT_PATH}) -readonly VARIANT=$(lsb_release -c | awk '{print $2}') - -docker build \ - --build-arg VARIANT=${VARIANT} \ - --memory 4G --cpu-quota 50000 \ - -t wamr_dev_${VARIANT}:0.1 -f "${ROOT}"/.devcontainer/Dockerfile "${ROOT}"/.devcontainer && - docker run --rm -it \ - --memory 4G \ - --cpus ".5" \ - --name workload_build_env \ - --mount type=bind,source="${ROOT}",target=/workspace \ - wamr_dev_${VARIANT}:0.1 \ - /bin/bash -c "\ - pwd \ - && pushd ${CURRENT_RELATIVE_ROOT} \ - && rm -rf build \ - && mkdir build \ - && pushd build \ - && cmake .. \ - && cmake --build . --config Release \ - && popd \ - && popd \ - && echo 'Go and find out results under ${CURRENT_RELATIVE_ROOT}/build' " diff --git a/samples/workload/include/.gitkeep b/samples/workload/include/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/samples/workload/meshoptimizer/CMakeLists.txt b/samples/workload/meshoptimizer/CMakeLists.txt index 172de8d67..263f6a182 100644 --- a/samples/workload/meshoptimizer/CMakeLists.txt +++ b/samples/workload/meshoptimizer/CMakeLists.txt @@ -1,11 +1,19 @@ # Copyright (C) 2019 Intel Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -cmake_minimum_required (VERSION 3.0) +cmake_minimum_required (VERSION 3.14) project(bench-meshoptimizer) -include(${CMAKE_CURRENT_SOURCE_DIR}/../cmake/preparation.cmake) +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../cmake) + +################ dependencies ################ +find_package(Python3 REQUIRED) +find_package(WASISDK 16.0 REQUIRED) +execute_process( + COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_LIST_DIR}/../../../test-tools/pick-up-emscripten-headers/collect_files.py --install ../include --loglevel=ERROR + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} +) ################ MESHOPTIMIZER ################ include(ExternalProject) @@ -13,7 +21,7 @@ include(ExternalProject) ExternalProject_Add(codecbench PREFIX codecbench GIT_REPOSITORY https://github.com/zeux/meshoptimizer.git - GIT_TAG master + GIT_TAG f734fd572aed5bf76e84d9ed62ca6f4f6c47d84e GIT_SHALLOW ON GIT_PROGRESS ON SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/meshoptimizer @@ -21,10 +29,10 @@ ExternalProject_Add(codecbench && ${CMAKE_COMMAND} -E echo "Applying patch" && git apply ${CMAKE_CURRENT_SOURCE_DIR}/codecbench.patch CONFIGURE_COMMAND ${CMAKE_COMMAND} - -DWASI_SDK_PREFIX=${WASI_SDK_HOME} - -DCMAKE_TOOLCHAIN_FILE=${WASI_SDK_HOME}/share/cmake/wasi-sdk.cmake - -DCMAKE_SYSROOT=${WASI_SDK_HOME}/share/wasi-sysroot + -DWASI_SDK_PREFIX=${WASISDK_HOME} + -DCMAKE_TOOLCHAIN_FILE=${WASISDK_TOOLCHAIN} + -DCMAKE_SYSROOT=${WASISDK_SYSROOT} ${CMAKE_CURRENT_SOURCE_DIR}/meshoptimizer - BUILD_COMMAND make codecbench - INSTALL_COMMAND ${CMAKE_COMMAND} -E copy ./codecbench.wasm ${CMAKE_BINARY_DIR}/codecbench.wasm + BUILD_COMMAND make codecbench -j 4 + INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_if_different ./codecbench.wasm ${CMAKE_CURRENT_BINARY_DIR}/codecbench.wasm ) diff --git a/samples/workload/meshoptimizer/README.md b/samples/workload/meshoptimizer/README.md index ceefb5723..466cd8759 100644 --- a/samples/workload/meshoptimizer/README.md +++ b/samples/workload/meshoptimizer/README.md @@ -44,14 +44,14 @@ Firstly please build iwasm with simd support: ``` shell $ cd /product-mini/platforms/linux/ $ mkdir build && cd build -$ cmake .. -DWAMR_BUILD_SIMD=1 +$ cmake .. $ make ``` Then compile wasm file to aot file and run: ``` shell -$ /wamr-compiler/build/wamrc --enable-simd -o codecbench.aot codecbench.wasm +$ /wamr-compiler/build/wamrc -o codecbench.aot codecbench.wasm $ /product-mini/platforms/linux/build/iwasm codecbench.aot ``` diff --git a/samples/workload/meshoptimizer/build_workload.sh b/samples/workload/meshoptimizer/build_workload.sh deleted file mode 120000 index a31afa928..000000000 --- a/samples/workload/meshoptimizer/build_workload.sh +++ /dev/null @@ -1 +0,0 @@ -../docker/build_workload.sh \ No newline at end of file diff --git a/samples/workload/meshoptimizer/codecbench.patch b/samples/workload/meshoptimizer/codecbench.patch index 667b8ab28..19db792bf 100644 --- a/samples/workload/meshoptimizer/codecbench.patch +++ b/samples/workload/meshoptimizer/codecbench.patch @@ -1,8 +1,8 @@ diff --git a/CMakeLists.txt b/CMakeLists.txt -index b13d946..4254003 100644 +index 612cf3b..22a365a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt -@@ -149,3 +149,43 @@ install(FILES +@@ -158,3 +158,43 @@ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/meshoptimizerConfigVersion.cmake COMPONENT meshoptimizer DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/meshoptimizer) @@ -47,10 +47,10 @@ index b13d946..4254003 100644 + +add_dependencies(codecbench.opt codecbench) diff --git a/src/vertexcodec.cpp b/src/vertexcodec.cpp -index 821c467..b7d30b1 100644 +index 4bd1112..257c258 100644 --- a/src/vertexcodec.cpp +++ b/src/vertexcodec.cpp -@@ -83,13 +83,13 @@ +@@ -89,13 +89,13 @@ #endif #ifdef SIMD_WASM @@ -71,7 +71,7 @@ index 821c467..b7d30b1 100644 #endif namespace meshopt -@@ -691,7 +691,7 @@ static v128_t decodeShuffleMask(unsigned char mask0, unsigned char mask1) +@@ -757,7 +757,7 @@ static v128_t decodeShuffleMask(unsigned char mask0, unsigned char mask1) v128_t sm1 = wasm_v128_load(&kDecodeBytesGroupShuffle[mask1]); v128_t sm1off = wasm_v128_load(&kDecodeBytesGroupCount[mask0]); @@ -80,7 +80,7 @@ index 821c467..b7d30b1 100644 v128_t sm1r = wasm_i8x16_add(sm1, sm1off); -@@ -741,7 +741,7 @@ static const unsigned char* decodeBytesGroupSimd(const unsigned char* data, unsi +@@ -807,7 +807,7 @@ static const unsigned char* decodeBytesGroupSimd(const unsigned char* data, unsi v128_t shuf = decodeShuffleMask(mask0, mask1); @@ -89,7 +89,7 @@ index 821c467..b7d30b1 100644 wasm_v128_store(buffer, result); -@@ -763,7 +763,7 @@ static const unsigned char* decodeBytesGroupSimd(const unsigned char* data, unsi +@@ -829,7 +829,7 @@ static const unsigned char* decodeBytesGroupSimd(const unsigned char* data, unsi v128_t shuf = decodeShuffleMask(mask0, mask1); @@ -99,7 +99,7 @@ index 821c467..b7d30b1 100644 wasm_v128_store(buffer, result); diff --git a/src/vertexfilter.cpp b/src/vertexfilter.cpp -index 14a73b1..8f4b3c1 100644 +index 5c7589c..c79cad4 100644 --- a/src/vertexfilter.cpp +++ b/src/vertexfilter.cpp @@ -57,10 +57,10 @@ @@ -116,4 +116,4 @@ index 14a73b1..8f4b3c1 100644 +#define wasmx_unziphi_v32x4(a, b) wasm_i32x4_shuffle(a, b, 1, 3, 5, 7) #endif - namespace meshopt + #ifndef __has_builtin diff --git a/samples/workload/preparation.sh b/samples/workload/preparation.sh index 3f49f0cf6..47b11ac56 100755 --- a/samples/workload/preparation.sh +++ b/samples/workload/preparation.sh @@ -5,13 +5,13 @@ # readonly BUILD_CONTENT="/tmp/build_content" -readonly WABT_VER=1.0.23 +readonly WABT_VER=1.0.31 readonly WABT_FILE="wabt-${WABT_VER}-ubuntu.tar.gz" -readonly CMAKE_VER=3.16.2 +readonly CMAKE_VER=3.25.1 readonly CMAKE_FILE="cmake-${CMAKE_VER}-Linux-x86_64.sh" -readonly BINARYEN_VER=version_101 +readonly BINARYEN_VER=version_111 readonly BINARYEN_FILE="binaryen-${BINARYEN_VER}-x86_64-linux.tar.gz" -readonly BAZEL_VER=3.7.0 +readonly BAZEL_VER=6.0.0 readonly BAZEL_FILE=bazel-${BAZEL_VER}-installer-linux-x86_64.sh function DEBUG() { @@ -57,8 +57,8 @@ function install_emsdk() { git clone https://github.com/emscripten-core/emsdk.git cd emsdk git pull - ./emsdk install 2.0.26 - ./emsdk activate 2.0.26 + ./emsdk install 3.1.28 + ./emsdk activate 3.1.28 echo "source /opt/emsdk/emsdk_env.sh" >> "${HOME}"/.bashrc } diff --git a/samples/workload/tensorflow/build.sh b/samples/workload/tensorflow/build.sh index 7289e617b..d997113fd 100755 --- a/samples/workload/tensorflow/build.sh +++ b/samples/workload/tensorflow/build.sh @@ -98,11 +98,11 @@ make WAMRC_CMD="$(pwd)/wamrc" cd ${OUT_DIR} if [[ $1 == '--sgx' ]]; then - ${WAMRC_CMD} --enable-simd -sgx -o benchmark_model.aot benchmark_model.wasm + ${WAMRC_CMD} -sgx -o benchmark_model.aot benchmark_model.wasm elif [[ $1 == '--threads' ]]; then - ${WAMRC_CMD} --enable-simd --enable-multi-thread -o benchmark_model.aot benchmark_model.wasm + ${WAMRC_CMD} --enable-multi-thread -o benchmark_model.aot benchmark_model.wasm else - ${WAMRC_CMD} --enable-simd -o benchmark_model.aot benchmark_model.wasm + ${WAMRC_CMD} -o benchmark_model.aot benchmark_model.wasm fi # 4. build iwasm with pthread and libc_emcc enable @@ -112,14 +112,14 @@ fi if [[ $1 == '--sgx' ]]; then cd ${WAMR_PLATFORM_DIR}/linux-sgx rm -fr build && mkdir build - cd build && cmake .. -DWAMR_BUILD_SIMD=1 -DWAMR_BUILD_LIB_PTHREAD=1 -DWAMR_BUILD_LIBC_EMCC=1 + cd build && cmake .. -DWAMR_BUILD_LIB_PTHREAD=1 -DWAMR_BUILD_LIBC_EMCC=1 make cd ../enclave-sample make else cd ${WAMR_PLATFORM_DIR}/linux rm -fr build && mkdir build - cd build && cmake .. -DWAMR_BUILD_SIMD=1 -DWAMR_BUILD_LIB_PTHREAD=1 -DWAMR_BUILD_LIBC_EMCC=1 + cd build && cmake .. -DWAMR_BUILD_LIB_PTHREAD=1 -DWAMR_BUILD_LIBC_EMCC=1 make fi diff --git a/samples/workload/wasm-av1/CMakeLists.avx_wasm.txt b/samples/workload/wasm-av1/CMakeLists.avx_wasm.txt index d11496123..409665b3c 100644 --- a/samples/workload/wasm-av1/CMakeLists.avx_wasm.txt +++ b/samples/workload/wasm-av1/CMakeLists.avx_wasm.txt @@ -1,14 +1,14 @@ # Copyright (C) 2019 Intel Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -cmake_minimum_required (VERSION 2.8...3.16) +cmake_minimum_required (VERSION 3.14) project(testavx) -include(${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/preparation.cmake) +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../cmake) -# a workaround to let aom find our non-public headers -include_directories(${WASI_SDK_HOME}/share/wasi-sysroot/include/libc/musl) +################ dependencies ################ +find_package(Binaryen 111 REQUIRED) ################ AOM ################ set(ENABLE_CCACHE ON) @@ -62,7 +62,7 @@ add_dependencies(${PROJECT_NAME} aom) add_custom_target(${PROJECT_NAME}_opt ALL COMMAND - ${WASM_OPT} -Oz --enable-simd -o ${PROJECT_NAME}.opt.wasm ${PROJECT_NAME}.wasm + ${Binaryen_WASM_OPT} -Oz --enable-simd -o ${PROJECT_NAME}.opt.wasm ${PROJECT_NAME}.wasm BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.opt.wasm WORKING_DIRECTORY diff --git a/samples/workload/wasm-av1/CMakeLists.txt b/samples/workload/wasm-av1/CMakeLists.txt index 8b1f0df0c..3d263bfbe 100644 --- a/samples/workload/wasm-av1/CMakeLists.txt +++ b/samples/workload/wasm-av1/CMakeLists.txt @@ -1,11 +1,19 @@ # Copyright (C) 2019 Intel Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -cmake_minimum_required (VERSION 2.8...3.16) +cmake_minimum_required (VERSION 3.14) project(av1_wasm) -include(${CMAKE_CURRENT_SOURCE_DIR}/../cmake/preparation.cmake) +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../cmake) + +################ dependencies ################ +find_package(Python3 REQUIRED) +find_package(WASISDK 16.0 REQUIRED) +execute_process( + COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_LIST_DIR}/../../../test-tools/pick-up-emscripten-headers/collect_files.py --install ../include --loglevel=ERROR + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} +) ####################################### include(ExternalProject) @@ -23,10 +31,14 @@ ExternalProject_Add(av1 && ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.avx_wasm.txt CMakeLists.txt && git apply ../av1-clang.patch CONFIGURE_COMMAND ${CMAKE_COMMAND} - -DWASI_SDK_PREFIX=${WASI_SDK_HOME} - -DCMAKE_TOOLCHAIN_FILE=${WASI_SDK_HOME}/share/cmake/wasi-sdk.cmake - -DCMAKE_SYSROOT=${WASI_SDK_HOME}/share/wasi-sysroot + -DWASI_SDK_PREFIX=${WASISDK_HOME} + -DCMAKE_TOOLCHAIN_FILE=${WASISDK_TOOLCHAIN} + -DCMAKE_SYSROOT=${WASISDK_SYSROOT} + -DCMAKE_C_FLAGS=-isystem\ ${CMAKE_CURRENT_SOURCE_DIR}/../include/sse\ -isystem\ ${CMAKE_CURRENT_SOURCE_DIR}/../include/libc/musl ${CMAKE_CURRENT_SOURCE_DIR}/av1 - BUILD_COMMAND make testavx_opt - INSTALL_COMMAND ${CMAKE_COMMAND} -E copy testavx.opt.wasm ${CMAKE_CURRENT_BINARY_DIR}/testavx.wasm + BUILD_COMMAND make testavx_opt -j 4 + INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_if_different + testavx.opt.wasm + ${CMAKE_CURRENT_SOURCE_DIR}/av1/third_party/samples/elephants_dream_480p24.ivf + ${CMAKE_CURRENT_BINARY_DIR} ) diff --git a/samples/workload/wasm-av1/README.md b/samples/workload/wasm-av1/README.md index e7a92f2af..2166fe6ae 100644 --- a/samples/workload/wasm-av1/README.md +++ b/samples/workload/wasm-av1/README.md @@ -39,7 +39,7 @@ Firstly please build iwasm with simd support: ``` shell $ cd /product-mini/platforms/linux/ $ mkdir build && cd build -$ cmake .. -DWAMR_BUILD_SIMD=1 -DWAMR_BUILD_LIBC_EMCC=1 +$ cmake .. -DWAMR_BUILD_LIBC_EMCC=1 $ make ``` @@ -47,7 +47,7 @@ Then compile wasm file to aot file and run: ``` shell $ cd -$ /wamr-compiler/build/wamrc --enable-simd -o testavx.aot testavx.wasm +$ /wamr-compiler/build/wamrc -o testavx.aot testavx.wasm # copy sample data like /samples/workload/wasm-av1/av1/third_party/samples/elephants_dream_480p24.ivf # make sure you declare the access priority of the directory in which the sample data is $ /product-mini/platforms/linux/build/iwasm --dir=. testavx.aot elephants_dream_480p24.ivf diff --git a/samples/workload/wasm-av1/build.sh b/samples/workload/wasm-av1/build.sh index 7f82c6c52..efa17eca6 100755 --- a/samples/workload/wasm-av1/build.sh +++ b/samples/workload/wasm-av1/build.sh @@ -85,12 +85,12 @@ cd build && cmake .. make # 3.2 compile wasm-av1.wasm to wasm-av1.aot cd ${OUT_DIR} -${WAMRC_CMD} --enable-simd -o testavx.aot testavx.wasm +${WAMRC_CMD} -o testavx.aot testavx.wasm # 4. build iwasm with pthread and libc_emcc enable cd ${WAMR_PLATFORM_DIR}/linux rm -fr build && mkdir build -cd build && cmake .. -DWAMR_BUILD_SIMD=1 -DWAMR_BUILD_LIB_PTHREAD=1 -DWAMR_BUILD_LIBC_EMCC=1 +cd build && cmake .. -DWAMR_BUILD_LIB_PTHREAD=1 -DWAMR_BUILD_LIBC_EMCC=1 make # 5. run wasm-av1 with iwasm diff --git a/samples/workload/wasm-av1/build_workload.sh b/samples/workload/wasm-av1/build_workload.sh deleted file mode 120000 index a31afa928..000000000 --- a/samples/workload/wasm-av1/build_workload.sh +++ /dev/null @@ -1 +0,0 @@ -../docker/build_workload.sh \ No newline at end of file diff --git a/test-tools/IoT-APP-Store-Demo/wasm_django/devices/templates/help.html b/test-tools/IoT-APP-Store-Demo/wasm_django/devices/templates/help.html index 9e9d63c9a..4ad7427ba 100755 --- a/test-tools/IoT-APP-Store-Demo/wasm_django/devices/templates/help.html +++ b/test-tools/IoT-APP-Store-Demo/wasm_django/devices/templates/help.html @@ -33,7 +33,7 @@

1. Download a simple runtime (build for ubuntu 20.04 64 bits, other platforms please build - from the source code) + from the source code)

2. In the terminal: cd ~/Download && ./simple -a 82.156.57.236 @@ -44,7 +44,7 @@

Notes:
We also have a UI-enabled runtime, please download here and enjoy. It may require + href="../static/upload/wasm_runtime_wgl">download here and enjoy. It may require a few more setups.

Before running the UI-enabled runtime, please install some required softwares:

sudo apt-get install libsdl2-2.0-0:i386

diff --git a/test-tools/IoT-APP-Store-Demo/wasm_django/server/wasm_server.py b/test-tools/IoT-APP-Store-Demo/wasm_django/server/wasm_server.py index 5edeb90aa..970ec6f60 100755 --- a/test-tools/IoT-APP-Store-Demo/wasm_django/server/wasm_server.py +++ b/test-tools/IoT-APP-Store-Demo/wasm_django/server/wasm_server.py @@ -16,14 +16,18 @@ import logging import os attr_type_list = [ - "ATTR_NONE", - "ATTR_TYPE_SHORT", - "ATTR_TYPE_INT", + "ATTR_TYPE_BYTE", # = ATTR_TYPE_INT8 + "ATTR_TYPE_SHORT",# = ATTR_TYPE_INT16 + "ATTR_TYPE_INT", # = ATTR_TYPE_INT32 "ATTR_TYPE_INT64", - "ATTR_TYPE_BYTE", + "ATTR_TYPE_UINT8", "ATTR_TYPE_UINT16", + "ATTR_TYPE_UINT32", + "ATTR_TYPE_UINT64", "ATTR_TYPE_FLOAT", "ATTR_TYPE_DOUBLE", + "ATTR_NONE", + "ATTR_NONE", "ATTR_TYPE_BOOLEAN", "ATTR_TYPE_STRING", "ATTR_TYPE_BYTEARRAY" @@ -140,26 +144,38 @@ def decode_attr_container(msg): attr_type = attr_type_list[int(type_index[0])] buf = buf[1 : ] - if attr_type == "ATTR_TYPE_SHORT": + if attr_type == "ATTR_TYPE_BYTE": # = ATTR_TYPE_INT8 + (attr_value, ) = struct.unpack('@c', buf[0 : 1]) + buf = buf[1 : ] + # continue + elif attr_type == "ATTR_TYPE_SHORT": # = ATTR_TYPE_INT16 (attr_value, ) = struct.unpack('@h', buf[0 : 2]) buf = buf[2 : ] # continue - elif attr_type == "ATTR_TYPE_INT": - (attr_value, ) = struct.unpack('@I', buf[0 : 4]) + elif attr_type == "ATTR_TYPE_INT": # = ATTR_TYPE_INT32 + (attr_value, ) = struct.unpack('@i', buf[0 : 4]) buf = buf[4 : ] # continue elif attr_type == "ATTR_TYPE_INT64": (attr_value, ) = struct.unpack('@q', buf[0 : 8]) buf = buf[8 : ] # continue - elif attr_type == "ATTR_TYPE_BYTE": - (attr_value, ) = struct.unpack('@c', buf[0 : 1]) + elif attr_type == "ATTR_TYPE_UINT8": + (attr_value, ) = struct.unpack('@B', buf[0 : 1]) buf = buf[1 : ] # continue elif attr_type == "ATTR_TYPE_UINT16": (attr_value, ) = struct.unpack('@H', buf[0 : 2]) buf = buf[2 : ] # continue + elif attr_type == "ATTR_TYPE_UINT32": + (attr_value, ) = struct.unpack('@I', buf[0 : 4]) + buf = buf[4 : ] + # continue + elif attr_type == "ATTR_TYPE_UINT64": + (attr_value, ) = struct.unpack('@Q', buf[0 : 8]) + buf = buf[8 : ] + # continue elif attr_type == "ATTR_TYPE_FLOAT": (attr_value, ) = struct.unpack('@f', buf[0 : 4]) buf = buf[4 : ] diff --git a/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/connection.wasm b/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/connection.wasm index 2a2e79dc7..936e80cf6 100644 Binary files a/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/connection.wasm and b/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/connection.wasm differ diff --git a/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/event_publisher.wasm b/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/event_publisher.wasm index 4cf911099..31dc1018f 100644 Binary files a/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/event_publisher.wasm and b/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/event_publisher.wasm differ diff --git a/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/event_subscriber.wasm b/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/event_subscriber.wasm index 1766c2565..1dce622f4 100644 Binary files a/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/event_subscriber.wasm and b/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/event_subscriber.wasm differ diff --git a/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/request_handler.wasm b/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/request_handler.wasm index 1da74dbe7..85959790f 100644 Binary files a/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/request_handler.wasm and b/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/request_handler.wasm differ diff --git a/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/request_sender.wasm b/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/request_sender.wasm index f7c9b994e..ce9a6f6e8 100644 Binary files a/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/request_sender.wasm and b/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/request_sender.wasm differ diff --git a/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/sensor.wasm b/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/sensor.wasm index eb51ec8ee..f24bd1009 100644 Binary files a/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/sensor.wasm and b/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/sensor.wasm differ diff --git a/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/simple b/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/simple index da9c485ed..478a0f85f 100755 Binary files a/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/simple and b/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/simple differ diff --git a/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/sys/connection.wasm b/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/sys/connection.wasm index 2a2e79dc7..936e80cf6 100644 Binary files a/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/sys/connection.wasm and b/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/sys/connection.wasm differ diff --git a/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/sys/event_publisher.wasm b/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/sys/event_publisher.wasm index 4cf911099..31dc1018f 100644 Binary files a/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/sys/event_publisher.wasm and b/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/sys/event_publisher.wasm differ diff --git a/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/sys/event_subscriber.wasm b/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/sys/event_subscriber.wasm index 1766c2565..1dce622f4 100644 Binary files a/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/sys/event_subscriber.wasm and b/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/sys/event_subscriber.wasm differ diff --git a/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/sys/request_handler.wasm b/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/sys/request_handler.wasm index 1da74dbe7..85959790f 100644 Binary files a/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/sys/request_handler.wasm and b/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/sys/request_handler.wasm differ diff --git a/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/sys/request_sender.wasm b/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/sys/request_sender.wasm index f7c9b994e..ce9a6f6e8 100644 Binary files a/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/sys/request_sender.wasm and b/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/sys/request_sender.wasm differ diff --git a/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/sys/timer.wasm b/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/sys/timer.wasm index bcd4c8fa4..0bb3c920f 100644 Binary files a/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/sys/timer.wasm and b/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/sys/timer.wasm differ diff --git a/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/timer.wasm b/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/timer.wasm index bcd4c8fa4..0bb3c920f 100644 Binary files a/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/timer.wasm and b/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/timer.wasm differ diff --git a/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/ui_app.wasm b/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/ui_app.wasm index ddda9e2d3..1182ca291 100644 Binary files a/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/ui_app.wasm and b/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/ui_app.wasm differ diff --git a/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/vgl_wasm_runtime b/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/vgl_wasm_runtime deleted file mode 100755 index fa68f3a2d..000000000 Binary files a/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/vgl_wasm_runtime and /dev/null differ diff --git a/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/wasm_runtime_wgl b/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/wasm_runtime_wgl index eb7b42b82..30f8e92e2 100755 Binary files a/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/wasm_runtime_wgl and b/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/wasm_runtime_wgl differ diff --git a/test-tools/build-wasi-sdk/patches/wasi_libc.patch b/test-tools/build-wasi-sdk/patches/wasi_libc.patch deleted file mode 100644 index e236735b4..000000000 --- a/test-tools/build-wasi-sdk/patches/wasi_libc.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/expected/wasm32-wasi/predefined-macros.txt b/expected/wasm32-wasi/predefined-macros.txt -index c1bb19e..954f3b5 100644 ---- a/expected/wasm32-wasi/predefined-macros.txt -+++ b/expected/wasm32-wasi/predefined-macros.txt -@@ -3002,6 +3002,8 @@ - #define __alignof_is_defined 1 - #define __bitop(x,i,o) ((x)[(i)/8] o (1<<(i)%8)) - #define __bool_true_false_are_defined 1 -+#define __clang_literal_encoding__ "UTF-8" -+#define __clang_wide_literal_encoding__ "UTF-32" - #define __inline inline - #define __restrict restrict - #define __tg_complex(fun,x) (__RETCAST_CX(x)( __FLTCX((x)+I) && __IS_FP(x) ? fun ## f (x) : __LDBLCX((x)+I) ? fun ## l (x) : fun(x) )) diff --git a/test-tools/build-wasi-sdk/patches/wasi_sdk.patch b/test-tools/build-wasi-sdk/patches/wasi_sdk.patch deleted file mode 100644 index 0fc4caee7..000000000 --- a/test-tools/build-wasi-sdk/patches/wasi_sdk.patch +++ /dev/null @@ -1,15 +0,0 @@ -diff --git a/version.sh b/version.sh -index 8e7c44c..ff0d3ba 100755 ---- a/version.sh -+++ b/version.sh -@@ -1,5 +1,6 @@ - #!/usr/bin/env bash --set -e --GIT_DESCR=$(git describe --long --candidates=999 --match='wasi-sdk-*' --dirty='+m' --abbrev=12) --GIT_PACKAGE_VERSION=$(echo $GIT_DESCR | perl -ne 'if(/^wasi-sdk-(\d+)-(\d+)-g([0-9a-f]{7,12})([+]m)?$/) { if($2 == 0) { print "$1.$2$4" } else { print "$1.$2g$3$4" } exit } else { print "could not parse git description"; exit 1 }';) --echo $GIT_PACKAGE_VERSION -+#set -e -+#GIT_DESCR=$(git describe --long --candidates=999 --match='wasi-sdk-*' --dirty='+m' --abbrev=12) -+#GIT_PACKAGE_VERSION=$(echo $GIT_DESCR | perl -ne 'if(/^wasi-sdk-(\d+)-(\d+)-g([0-9a-f]{7,12})([+]m)?$/) { if($2 == 0) { print "$1.$2$4" } else { print "$1.$2g$3$4" } exit } else { print "could not parse git description"; exit 1 }';) -+#echo $GIT_PACKAGE_VERSION -+echo wasi-sdk-13-eng diff --git a/test-tools/host-tool/src/host_tool_utils.c b/test-tools/host-tool/src/host_tool_utils.c index 2b9051bbf..9ea3d6ca9 100644 --- a/test-tools/host-tool/src/host_tool_utils.c +++ b/test-tools/host-tool/src/host_tool_utils.c @@ -15,11 +15,14 @@ typedef union jvalue { bool z; - int8_t b; - uint16_t c; - int16_t s; - int32_t i; - int64_t j; + int8_t i8; + uint8_t u8; + int16_t i16; + uint16_t u16; + int32_t i32; + uint32_t u32; + int64_t i64; + uint64_t u64; float f; double d; } jvalue; @@ -90,43 +93,64 @@ attr2json(const attr_container_t *attr_cont) type = *p++; switch (type) { - case ATTR_TYPE_SHORT: - bh_memcpy_s(&value.s, sizeof(int16_t), p, sizeof(int16_t)); - if (NULL == (obj = cJSON_CreateNumber(value.s))) + case ATTR_TYPE_BYTE: /* = ATTR_TYPE_INT8 */ + bh_memcpy_s(&value.i8, 1, p, 1); + if (NULL == (obj = cJSON_CreateNumber(value.i8))) + goto fail; + cJSON_AddItemToObject(root, key, obj); + p++; + break; + case ATTR_TYPE_SHORT: /* = ATTR_TYPE_INT16 */ + bh_memcpy_s(&value.i16, sizeof(int16_t), p, sizeof(int16_t)); + if (NULL == (obj = cJSON_CreateNumber(value.i16))) goto fail; cJSON_AddItemToObject(root, key, obj); /* another approach: cJSON_AddNumberToObject(root, key, value.s) */ p += 2; break; - case ATTR_TYPE_INT: - bh_memcpy_s(&value.i, sizeof(int32_t), p, sizeof(int32_t)); - if (NULL == (obj = cJSON_CreateNumber(value.i))) + case ATTR_TYPE_INT: /* = ATTR_TYPE_INT32 */ + bh_memcpy_s(&value.i32, sizeof(int32_t), p, sizeof(int32_t)); + if (NULL == (obj = cJSON_CreateNumber(value.i32))) goto fail; cJSON_AddItemToObject(root, key, obj); p += 4; break; case ATTR_TYPE_INT64: - bh_memcpy_s(&value.j, sizeof(uint64_t), p, sizeof(uint64_t)); - if (NULL == (obj = cJSON_CreateNumber(value.j))) + bh_memcpy_s(&value.i64, sizeof(int64_t), p, sizeof(int64_t)); + if (NULL == (obj = cJSON_CreateNumber(value.i64))) goto fail; cJSON_AddItemToObject(root, key, obj); p += 8; break; - case ATTR_TYPE_BYTE: - bh_memcpy_s(&value.b, 1, p, 1); - if (NULL == (obj = cJSON_CreateNumber(value.b))) + case ATTR_TYPE_UINT8: + bh_memcpy_s(&value.u8, 1, p, 1); + if (NULL == (obj = cJSON_CreateNumber(value.u8))) goto fail; cJSON_AddItemToObject(root, key, obj); p++; break; case ATTR_TYPE_UINT16: - bh_memcpy_s(&value.c, sizeof(uint16_t), p, sizeof(uint16_t)); - if (NULL == (obj = cJSON_CreateNumber(value.c))) + bh_memcpy_s(&value.u16, sizeof(uint16_t), p, sizeof(uint16_t)); + if (NULL == (obj = cJSON_CreateNumber(value.u16))) goto fail; cJSON_AddItemToObject(root, key, obj); p += 2; break; + case ATTR_TYPE_UINT32: + bh_memcpy_s(&value.u32, sizeof(uint32_t), p, sizeof(uint32_t)); + if (NULL == (obj = cJSON_CreateNumber(value.u32))) + goto fail; + cJSON_AddItemToObject(root, key, obj); + p += 4; + break; + case ATTR_TYPE_UINT64: + bh_memcpy_s(&value.u64, sizeof(uint64_t), p, sizeof(uint64_t)); + if (NULL == (obj = cJSON_CreateNumber(value.u64))) + goto fail; + cJSON_AddItemToObject(root, key, obj); + p += 8; + break; case ATTR_TYPE_FLOAT: bh_memcpy_s(&value.f, sizeof(float), p, sizeof(float)); if (NULL == (obj = cJSON_CreateNumber(value.f))) diff --git a/test-tools/build-wasi-sdk/build_wasi_sdk.py b/test-tools/pick-up-emscripten-headers/collect_files.py similarity index 50% rename from test-tools/build-wasi-sdk/build_wasi_sdk.py rename to test-tools/pick-up-emscripten-headers/collect_files.py index a87cdef69..7e832145f 100755 --- a/test-tools/build-wasi-sdk/build_wasi_sdk.py +++ b/test-tools/pick-up-emscripten-headers/collect_files.py @@ -9,68 +9,35 @@ The script operates on such directories and files |-- core | `-- deps | |-- emscripten -| `-- wasi-sdk -| `-- src -| |-- llvm-project -| `-- wasi-libc +|-- samples +| `-- workloads +| |-- include `-- test-tools - |-- build-wasi-sdk - | |-- build_wasi_sdk.py - | |-- include - | `-- patches - `-- wasi-sdk - |-- bin - |-- lib - `-- share - `-- wasi-sysroot + |-- pick-up-emscripten_headers + | |-- collect_files.py """ +import argparse import hashlib import logging import os import pathlib -import shlex import shutil -import subprocess import sys import tarfile import tempfile import urllib import urllib.request -logger = logging.getLogger("build_wasi_sdk") +logger = logging.getLogger("pick-up-emscripten-headers") external_repos = { - "config": { - "sha256": "302e5e7f3c4996976c58efde8b2f28f71d51357e784330eeed738e129300dc33", - "store_dir": "core/deps/wasi-sdk/src/config", - "strip_prefix": "config-191bcb948f7191c36eefe634336f5fc5c0c4c2be", - "url": "https://git.savannah.gnu.org/cgit/config.git/snapshot/config-191bcb948f7191c36eefe634336f5fc5c0c4c2be.tar.gz", - }, "emscripten": { - "sha256": "0904a65379aea3ea94087b8c12985b2fee48599b473e3bef914fec2e3941532d", + "sha256": "c5524755b785d8f4b83eb3214fdd3ac4b2e1b1a4644df4c63f06e5968f48f90e", "store_dir": "core/deps/emscripten", - "strip_prefix": "emscripten-2.0.28", - "url": "https://github.com/emscripten-core/emscripten/archive/refs/tags/2.0.28.tar.gz", - }, - "llvm-project": { - "sha256": "dc5169e51919f2817d06615285e9da6a804f0f881dc55d6247baa25aed3cc143", - "store_dir": "core/deps/wasi-sdk/src/llvm-project", - "strip_prefix": "llvm-project-34ff6a75f58377f32a5046a29f55c4c0e58bee9e", - "url": "https://github.com/llvm/llvm-project/archive/34ff6a75f58377f32a5046a29f55c4c0e58bee9e.tar.gz", - }, - "wasi-sdk": { - "sha256": "fc4fdb0e97b915241f32209492a7d0fab42c24216f87c1d5d75f46f7c70a553d", - "store_dir": "core/deps/wasi-sdk", - "strip_prefix": "wasi-sdk-1a953299860bbcc198ad8c12a21d1b2e2f738355", - "url": "https://github.com/WebAssembly/wasi-sdk/archive/1a953299860bbcc198ad8c12a21d1b2e2f738355.tar.gz", - }, - "wasi-libc": { - "sha256": "f6316ca9479d3463eb1c4f6a1d1f659bf15f67cb3c1e2e83d9d11f188dccd864", - "store_dir": "core/deps/wasi-sdk/src/wasi-libc", - "strip_prefix": "wasi-libc-a78cd329aec717f149934d7362f57050c9401f60", - "url": "https://github.com/WebAssembly/wasi-libc/archive/a78cd329aec717f149934d7362f57050c9401f60.tar.gz", - }, + "strip_prefix": "emscripten-3.0.0", + "url": "https://github.com/emscripten-core/emscripten/archive/refs/tags/3.0.0.tar.gz", + } } # TOOD: can we use headers from wasi-libc and clang directly ? @@ -111,25 +78,25 @@ def unpack(tar_file, strip_prefix, dest_dir): with tempfile.TemporaryDirectory() as tmp: with tarfile.open(tar_file) as tar: logger.debug(f"extract to {tmp}") + def is_within_directory(directory, target): - + abs_directory = os.path.abspath(directory) abs_target = os.path.abspath(target) - + prefix = os.path.commonprefix([abs_directory, abs_target]) - + return prefix == abs_directory - + def safe_extract(tar, path=".", members=None, *, numeric_owner=False): - + for member in tar.getmembers(): member_path = os.path.join(path, member.name) if not is_within_directory(path, member_path): raise Exception("Attempted Path Traversal in Tar File") - - tar.extractall(path, members, numeric_owner=numeric_owner) - - + + tar.extractall(path, members, numeric_owner=numeric_owner) + safe_extract(tar, tmp) strip_prefix_dir = ( @@ -160,12 +127,12 @@ def download_repo(name, root): download_flag = store_dir.joinpath("DOWNLOADED") if store_dir.exists() and download_flag.exists(): logger.info( - f"keep using '{store_dir.relative_to(root)}'. Or to remove it and try again" + f"bypass downloading '{store_dir.relative_to(root)}'. Or to remove it and try again if needs a new release" ) return True # download only when the target is neither existed nor broken - download_dir = pathlib.Path("/tmp/build_wasi_sdk/") + download_dir = pathlib.Path("/tmp/pick-up-emscripten-headers/") download_dir.mkdir(exist_ok=True) tar_name = pathlib.Path(external_repos[name]["url"]).name @@ -192,104 +159,67 @@ def download_repo(name, root): download_flag.touch() # leave download files in /tmp + logger.info(f"Has downloaed and stored in {store_dir.relative_to(root)}") return True -def run_patch(patch_file, cwd): - if not patch_file.exists(): - logger.error(f"{patch_file} not found") +def collect_headers(root, install_location): + if not install_location.exists(): + logger.error(f"{install_location} does not found") return False - with open(patch_file, "r") as f: - try: - PATCH_DRY_RUN_CMD = "patch -f -p1 --dry-run" - if subprocess.check_call(shlex.split(PATCH_DRY_RUN_CMD), stdin=f, cwd=cwd): - logger.error(f"patch dry-run {cwd} failed") - return False + install_flag = install_location.joinpath("INSTALLED").resolve() + if install_flag.exists(): + logger.info( + f"bypass downloading '{install_location}'. Or to remove it and try again if needs a new one" + ) + return True - PATCH_CMD = "patch -f -p1" - f.seek(0) - if subprocess.check_call(shlex.split(PATCH_CMD), stdin=f, cwd=cwd): - logger.error(f"patch {cwd} failed") - return False - except subprocess.CalledProcessError: - logger.error(f"patch {cwd} failed") - return False - return True - - -def build_and_install_wasi_sdk(root): - store_dir = root.joinpath(f'{external_repos["wasi-sdk"]["store_dir"]}').resolve() - if not store_dir.exists(): - logger.error(f"{store_dir} does not found") + emscripten_home = root.joinpath( + f'{external_repos["emscripten"]["store_dir"]}' + ).resolve() + if not emscripten_home.exists(): + logger.error(f"{emscripten_home} does not found") return False - # patch wasi-libc and wasi-sdk - patch_flag = store_dir.joinpath("PATCHED") - if not patch_flag.exists(): - if not run_patch( - root.joinpath("test-tools/build-wasi-sdk/patches/wasi_libc.patch"), - store_dir.joinpath("src/wasi-libc"), - ): - return False - - if not run_patch( - root.joinpath("test-tools/build-wasi-sdk/patches/wasi_sdk.patch"), store_dir - ): - return False - - patch_flag.touch() - else: - logger.info("bypass the patch phase") - - # build - build_flag = store_dir.joinpath("BUILDED") - if not build_flag.exists(): - BUILD_CMD = "make build" - if subprocess.check_call(shlex.split(BUILD_CMD), cwd=store_dir): - logger.error(f"build wasi-sdk failed") - return False - - build_flag.touch() - else: - logger.info("bypass the build phase") - - # install - install_flag = store_dir.joinpath("INSTALLED") - binary_path = root.joinpath("test-tools").resolve() - if not install_flag.exists(): - shutil.copytree( - str(store_dir.joinpath("build/install/opt").resolve()), - str(binary_path), - dirs_exist_ok=True, - ) - - # install headers - emscripten_headers = ( - root.joinpath(external_repos["emscripten"]["store_dir"]) - .joinpath("system") - .resolve() - ) - wasi_sysroot_headers = binary_path.joinpath( - "wasi-sdk/share/wasi-sysroot/include" - ).resolve() - for (src, dst) in emscripten_headers_src_dst: - src = emscripten_headers.joinpath(src) - dst = wasi_sysroot_headers.joinpath(dst) - dst.parent.mkdir(parents=True, exist_ok=True) - shutil.copy(src, dst) - - install_flag.touch() - else: - logger.info("bypass the install phase") + emscripten_headers = emscripten_home.joinpath("system").resolve() + for (src, dst) in emscripten_headers_src_dst: + src = emscripten_headers.joinpath(src) + dst = install_location.joinpath(dst) + dst.parent.mkdir(parents=True, exist_ok=True) + shutil.copy(src, dst) + install_flag.touch() + logger.info(f"Has installed in {install_location}") return True def main(): + parser = argparse.ArgumentParser( + description="collect headers from emscripten for workload compilation" + ) + parser.add_argument( + "--install", + type=str, + required=True, + help="identify installation location", + ) + parser.add_argument( + "--loglevel", + type=str, + default="INFO", + choices=[ + "ERROR", + "WARNING", + "INFO", + ], + help="the logging level", + ) + options = parser.parse_args() + console = logging.StreamHandler() console.setFormatter(logging.Formatter("%(asctime)s - %(message)s")) - logger.setLevel(logging.INFO) + logger.setLevel(getattr(logging, options.loglevel)) logger.addHandler(console) logger.propagate = False @@ -305,12 +235,9 @@ def main(): if not download_repo(repo, root): return False - # build wasi_sdk and install - if not build_and_install_wasi_sdk(root): + if not collect_headers(root, pathlib.Path(options.install)): return False - # TODO install headers from emscripten - return True diff --git a/test-tools/wamr-ide/VSCode-Extension/package.json b/test-tools/wamr-ide/VSCode-Extension/package.json index fa3292c23..71cc74871 100644 --- a/test-tools/wamr-ide/VSCode-Extension/package.json +++ b/test-tools/wamr-ide/VSCode-Extension/package.json @@ -1,12 +1,12 @@ { "name": "wamride", - "publisher": "wamr", + "publisher": "wamr-publisher", "repository": { "url": "https://github.com/bytecodealliance/wasm-micro-runtime/tree/main/test-tools/wamr-ide" }, "displayName": "WAMR-IDE", "description": "An Integrated Development Environment for WASM", - "version": "1.0.0", + "version": "1.1.2", "engines": { "vscode": "^1.59.0" }, diff --git a/test-tools/wamr-ide/VSCode-Extension/src/constants.ts b/test-tools/wamr-ide/VSCode-Extension/src/constants.ts new file mode 100644 index 000000000..cf8bb7103 --- /dev/null +++ b/test-tools/wamr-ide/VSCode-Extension/src/constants.ts @@ -0,0 +1,7 @@ +export const enum SelectionOfPrompt { + skip = 'skip', + setUp = 'setup', +} +export const enum Status { + done = 'done', +} diff --git a/test-tools/wamr-ide/VSCode-Extension/src/extension.ts b/test-tools/wamr-ide/VSCode-Extension/src/extension.ts index 17dda9496..9d979b7ac 100644 --- a/test-tools/wamr-ide/VSCode-Extension/src/extension.ts +++ b/test-tools/wamr-ide/VSCode-Extension/src/extension.ts @@ -24,6 +24,13 @@ import { getWAMRExtensionVersion, } from './utilities/lldbUtilities'; +import { + checkIfDockerStarted, + checkIfDockerImagesExist, + promptSetupDockerImages, +} from './utilities/dockerUtilities'; +import { SelectionOfPrompt } from './constants'; + let wasmTaskProvider: WasmTaskProvider; let wasmDebugConfigProvider: WasmDebugConfigurationProvider; let currentPrjDir = ''; @@ -304,7 +311,7 @@ export async function activate(context: vscode.ExtensionContext) { const disposableBuild = vscode.commands.registerCommand( 'wamride.build', - () => { + async () => { if (!isWasmProject) { vscode.window.showErrorMessage('Build failed', { modal: true, @@ -313,6 +320,28 @@ export async function activate(context: vscode.ExtensionContext) { return; } + try { + /* check if docker images are ready before building */ + if ( + (await checkIfDockerStarted()) && + !(await checkIfDockerImagesExist(context)) + ) { + /**NOTE - if users select to skip install, + * we should return rather than continue + * the execution + */ + if ( + (await promptSetupDockerImages(context)) === + SelectionOfPrompt.skip + ) { + return; + } + } + } catch (e) { + vscode.window.showWarningMessage((e as Error).message); + return; + } + generateCMakeFile(includePathArr, excludeFileArr); /* destroy the wasm-toolchain-ctr if it exists */ vscode.commands @@ -382,10 +411,35 @@ export async function activate(context: vscode.ExtensionContext) { /* we should check again whether the user installed lldb, as this can be skipped during activation */ try { if (!isLLDBInstalled(context)) { - await promptInstallLLDB(context); + /**NOTE - if users select to skip install, + * we should return rather than continue + * the execution + */ + if ( + (await promptInstallLLDB(context)) === + SelectionOfPrompt.skip + ) { + return; + } + } + + if ( + (await checkIfDockerStarted()) && + !(await checkIfDockerImagesExist(context)) + ) { + /**NOTE - save as above lldb, should return if + * users select to skip set up + */ + if ( + (await promptSetupDockerImages(context)) === + SelectionOfPrompt.skip + ) { + return; + } } } catch (e) { vscode.window.showWarningMessage((e as Error).message); + return; } /* refuse to debug if build process failed */ @@ -461,48 +515,70 @@ export async function activate(context: vscode.ExtensionContext) { } ); - const disposableRun = vscode.commands.registerCommand('wamride.run', () => { - if (!isWasmProject) { - vscode.window.showErrorMessage('run failed', { - modal: true, - detail: 'Current project is not wasm project, please open wasm project and try again.', - }); - return; - } - - /* refuse to debug if build process failed */ - if (!checkIfBuildSuccess()) { - vscode.window.showErrorMessage('Debug failed', { - modal: true, - detail: 'Can not find WASM binary, please build WASM firstly.', - }); - return; - } - vscode.commands - .executeCommand( - 'workbench.action.tasks.runTask', - 'Destroy: Wasm-Container-Before-Run' - ) - .then(() => { - const disposableAft = vscode.tasks.onDidEndTaskProcess(e => { - if (e.execution.task.name === 'Wasm-Container-Before-Run') { - /* make sure that run wasm task will be executed after destroy task finish */ - vscode.commands - .executeCommand( - 'workbench.action.tasks.runTask', - 'Run: Wasm' - ) - .then(() => { - if (e.exitCode !== 0) { - disposableAft.dispose(); - return; - } - }); - disposableAft.dispose(); - } + const disposableRun = vscode.commands.registerCommand( + 'wamride.run', + async () => { + if (!isWasmProject) { + vscode.window.showErrorMessage('run failed', { + modal: true, + detail: 'Current project is not wasm project, please open wasm project and try again.', }); - }); - }); + return; + } + + try { + /* check if docker images are set up before building */ + if ( + (await checkIfDockerStarted()) && + !(await checkIfDockerImagesExist(context)) + ) { + await promptSetupDockerImages(context); + } + } catch (e) { + vscode.window.showWarningMessage((e as Error).message); + return; + } + + /* refuse to debug if build process failed */ + if (!checkIfBuildSuccess()) { + vscode.window.showErrorMessage('Debug failed', { + modal: true, + detail: 'Can not find WASM binary, please build WASM firstly.', + }); + return; + } + + vscode.commands + .executeCommand( + 'workbench.action.tasks.runTask', + 'Destroy: Wasm-Container-Before-Run' + ) + .then(() => { + const disposableAft = vscode.tasks.onDidEndTaskProcess( + e => { + if ( + e.execution.task.name === + 'Wasm-Container-Before-Run' + ) { + /* make sure that run wasm task will be executed when destroy task finish */ + vscode.commands + .executeCommand( + 'workbench.action.tasks.runTask', + 'Run: Wasm' + ) + .then(() => { + if (e.exitCode !== 0) { + disposableAft.dispose(); + return; + } + }); + disposableAft.dispose(); + } + } + ); + }); + } + ); const disposableToggleIncludePath = vscode.commands.registerCommand( 'wamride.build.toggleStateIncludePath', @@ -700,6 +776,13 @@ export async function activate(context: vscode.ExtensionContext) { if (!isLLDBInstalled(context)) { await promptInstallLLDB(context); } + + if ( + (await checkIfDockerStarted()) && + !(await checkIfDockerImagesExist(context)) + ) { + await promptSetupDockerImages(context); + } } catch (e) { vscode.window.showWarningMessage((e as Error).message); } diff --git a/test-tools/wamr-ide/VSCode-Extension/src/utilities/dockerUtilities.ts b/test-tools/wamr-ide/VSCode-Extension/src/utilities/dockerUtilities.ts new file mode 100644 index 000000000..0a749ba19 --- /dev/null +++ b/test-tools/wamr-ide/VSCode-Extension/src/utilities/dockerUtilities.ts @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +import * as vscode from 'vscode'; +import * as cp from 'child_process'; +import * as path from 'path'; +import * as fs from 'fs'; +import { getWAMRExtensionVersion } from './lldbUtilities'; +import { downloadFile, unzipFile } from './directoryUtilities'; +import { SelectionOfPrompt, Status } from '../constants'; + +const DOCKER_IMAGES_TEM_FOLDER_NAME = 'docker-resource'; + +type SelectionStatus = SelectionOfPrompt | Status; + +const execShell = (cmd: string) => + new Promise((resolve, reject) => { + cp.exec(cmd, (error, result) => { + if (error) { + return reject(error); + } + return resolve(result); + }); + }); + +export async function promptSetupDockerImages( + context: vscode.ExtensionContext +): Promise { + const extensionPath = context.extensionPath; + const response = await vscode.window.showWarningMessage( + 'Necessary docker images are not found. Setup now?', + SelectionOfPrompt.setUp, + SelectionOfPrompt.skip + ); + + if (response === SelectionOfPrompt.skip) { + return response; + } + + const downloadUrlArray = getDockerImagesDownloadUrl(context); + + const destinationFolder = path.resolve( + extensionPath, + 'resource', + DOCKER_IMAGES_TEM_FOLDER_NAME + ); + + if (!fs.existsSync(destinationFolder)) { + fs.mkdirSync(destinationFolder); + } + + vscode.window.showInformationMessage(`Downloading Docker Images...`); + + for (const url of downloadUrlArray) { + const imageZipName = path.basename(url); + const imageStorePath = path.join(destinationFolder, imageZipName); + await downloadFile(url, imageStorePath); + + /** + * extract docker image tar package to + * '${destinationFolder}' + */ + const dockerImageFile = await unzipFile(imageStorePath, filename => + path.join(destinationFolder, filename) + ); + /* give access before loading */ + dockerImageFile.forEach(file => fs.chmodSync(file, '0775')); + + /**NOTE - load docker image tar package to host + * right now there are just one file + * `docker-image-name.tar` inside so we can + * directly use files[0] here, should be modified + * if the package's files change + */ + await execShell(`docker load -i ${dockerImageFile[0]}`); + } + + /* remove the DOCKER_IMAGES_TEM_FOLDER */ + fs.rmSync(destinationFolder, { recursive: true, force: true }); + + vscode.window.showInformationMessage( + `Docker images are ready, please run '$docker images' to check.` + ); + + return Status.done; +} + +export async function checkIfDockerStarted(): Promise { + try { + await execShell('docker images'); + return true; + } catch (e) { + vscode.window.showWarningMessage((e as Error).message); + return false; + } +} + +export async function checkIfDockerImagesExist( + context: vscode.ExtensionContext +): Promise { + try { + /* the tag of images is equal to extension's version */ + const imageTag = getWAMRExtensionVersion(context); + await execShell( + `docker image inspect wasm-debug-server:${imageTag} wasm-toolchain:${imageTag}` + ); + return true; + } catch (e) { + return false; + } +} + +function getDockerImagesDownloadUrl( + context: vscode.ExtensionContext +): string[] { + const wamrVersion = getWAMRExtensionVersion(context); + const wamrReleaseUrl = `https://github.com/bytecodealliance/wasm-micro-runtime/releases/download/WAMR`; + + return [ + `${wamrReleaseUrl}-${wamrVersion}/wasm-debug-server-${wamrVersion}.zip`, + `${wamrReleaseUrl}-${wamrVersion}/wasm-toolchain-${wamrVersion}.zip`, + ]; +} diff --git a/test-tools/wamr-ide/VSCode-Extension/src/utilities/lldbUtilities.ts b/test-tools/wamr-ide/VSCode-Extension/src/utilities/lldbUtilities.ts index a1729ab96..210ad0208 100644 --- a/test-tools/wamr-ide/VSCode-Extension/src/utilities/lldbUtilities.ts +++ b/test-tools/wamr-ide/VSCode-Extension/src/utilities/lldbUtilities.ts @@ -12,6 +12,7 @@ import { downloadFile, unzipFile, } from './directoryUtilities'; +import { SelectionOfPrompt, Status } from '../constants'; const LLDB_RESOURCE_DIR = 'resource/debug'; const LLDB_OS_DOWNLOAD_URL_SUFFIX_MAP: Partial< @@ -67,18 +68,17 @@ export function isLLDBInstalled(context: vscode.ExtensionContext): boolean { export async function promptInstallLLDB( context: vscode.ExtensionContext -): Promise { +): Promise { const extensionPath = context.extensionPath; - const setupPrompt = 'setup'; - const skipPrompt = 'skip'; + const response = await vscode.window.showWarningMessage( 'No LLDB instance found. Setup now?', - setupPrompt, - skipPrompt + SelectionOfPrompt.setUp, + SelectionOfPrompt.skip ); - if (response === skipPrompt) { - return; + if (response === SelectionOfPrompt.skip) { + return response; } const downloadUrl = getLLDBDownloadUrl(context); @@ -114,7 +114,6 @@ export async function promptInstallLLDB( ); // Remove the bundle.zip - fs.unlink(lldbZipPath, () => { - return; - }); + fs.unlinkSync(lldbZipPath); + return SelectionOfPrompt.setUp; } diff --git a/test-tools/wamr-ide/WASM-Toolchain/Docker/Dockerfile b/test-tools/wamr-ide/WASM-Toolchain/Docker/Dockerfile index f96a2261f..6ca210947 100644 --- a/test-tools/wamr-ide/WASM-Toolchain/Docker/Dockerfile +++ b/test-tools/wamr-ide/WASM-Toolchain/Docker/Dockerfile @@ -25,17 +25,17 @@ RUN wget --progress=dot:giga https://github.com/Kitware/CMake/releases/download/ # - wamr-sdk ## - download wasi-sdk with wget and set up to /opt/wasi-sdk -RUN wget --progress=dot:giga https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-14/wasi-sdk-14.0-linux.tar.gz \ - && tar -zxvf wasi-sdk-14.0-linux.tar.gz \ - && mv wasi-sdk-14.0 /opt/wasi-sdk/ \ - && rm -f wasi-sdk-14.0-linux.tar.gz +RUN wget --progress=dot:giga https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-19/wasi-sdk-19.0-linux.tar.gz \ + && tar -zxvf wasi-sdk-*-linux.tar.gz \ + && mv wasi-sdk-19.0 /opt/wasi-sdk/ \ + && rm -f wasi-sdk-*-linux.tar.gz ## - clone wamr repo -RUN git clone -b main --depth=1 https://github.com/bytecodealliance/wasm-micro-runtime.git +RUN git clone -b main --depth=1 https://github.com/bytecodealliance/wasm-micro-runtime.git WORKDIR /root/wasm-micro-runtime/wamr-compiler RUN ./build_llvm.sh \ - && mkdir build + && mkdir build WORKDIR /root/wasm-micro-runtime/wamr-compiler/build RUN cmake .. \ diff --git a/tests/wamr-test-suites/spec-test-script/all.py b/tests/wamr-test-suites/spec-test-script/all.py index 048835207..8b26d6892 100644 --- a/tests/wamr-test-suites/spec-test-script/all.py +++ b/tests/wamr-test-suites/spec-test-script/all.py @@ -14,6 +14,18 @@ import time """ The script itself has to be put under the same directory with the "spec". +To run a single non-GC case with interpreter mode: + cd workspace + python3 runtest.py --wast2wasm wabt/bin/wat2wasm --interpreter iwasm \ + spec/test/core/xxx.wast +To run a single non-GC case with aot mode: + cd workspace + python3 runtest.py --aot --wast2wasm wabt/bin/wat2wasm --interpreter iwasm \ + --aot-compiler wamrc spec/test/core/xxx.wast +To run a single GC case: + cd workspace + python3 runtest.py --wast2wasm spec/interpreter/wasm --interpreter iwasm \ + --aot-compiler wamrc --gc spec/test/core/xxx.wast """ PLATFORM_NAME = os.uname().sysname.lower() @@ -22,9 +34,9 @@ IWASM_SGX_CMD = "../../../product-mini/platforms/linux-sgx/enclave-sample/iwasm" IWASM_QEMU_CMD = "iwasm" SPEC_TEST_DIR = "spec/test/core" WAST2WASM_CMD = "./wabt/out/gcc/Release/wat2wasm" +SPEC_INTERPRETER_CMD = "spec/interpreter/wasm" WAMRC_CMD = "../../../wamr-compiler/build/wamrc" - class TargetAction(argparse.Action): TARGET_MAP = { "ARMV7_VFP": "armv7", @@ -51,6 +63,7 @@ def ignore_the_case( multi_module_flag=False, multi_thread_flag=False, simd_flag=False, + gc_flag=False, xip_flag=False, qemu_flag=False ): @@ -63,6 +76,10 @@ def ignore_the_case( if "i386" == target and case_name in ["float_exprs"]: return True + if gc_flag: + if case_name in ["type-canon", "type-equivalence", "type-rec"]: + return True; + if sgx_flag: if case_name in ["conversions", "f32_bitwise", "f64_bitwise"]: return True @@ -76,7 +93,9 @@ def ignore_the_case( return True if qemu_flag: - if case_name in ["f32_bitwise", "f64_bitwise", "loop", "f64", "f64_cmp", "conversions", "f32", "f32_cmp", "float_exprs", "float_misc", "select", "memory_grow"]: + if case_name in ["f32_bitwise", "f64_bitwise", "loop", "f64", "f64_cmp", + "conversions", "f32", "f32_cmp", "float_exprs", + "float_misc", "select", "memory_grow"]: return True return False @@ -109,8 +128,10 @@ def test_case( xip_flag=False, clean_up_flag=True, verbose_flag=True, + gc_flag=False, qemu_flag=False, qemu_firmware='', + log='', ): case_path = pathlib.Path(case_path).resolve() case_name = case_path.stem @@ -123,6 +144,7 @@ def test_case( multi_module_flag, multi_thread_flag, simd_flag, + gc_flag, xip_flag, qemu_flag ): @@ -130,7 +152,7 @@ def test_case( CMD = ["python3", "runtest.py"] CMD.append("--wast2wasm") - CMD.append(WAST2WASM_CMD) + CMD.append(WAST2WASM_CMD if not gc_flag else SPEC_INTERPRETER_CMD) CMD.append("--interpreter") if sgx_flag: CMD.append(IWASM_SGX_CMD) @@ -170,6 +192,13 @@ def test_case( if not clean_up_flag: CMD.append("--no_cleanup") + if gc_flag: + CMD.append("--gc") + + if log != '': + CMD.append("--log-dir") + CMD.append(log) + CMD.append(case_path) print(f"============> run {case_name} ", end="") with subprocess.Popen( @@ -226,9 +255,11 @@ def test_suite( xip_flag=False, clean_up_flag=True, verbose_flag=True, + gc_flag=False, parl_flag=False, qemu_flag=False, - qemu_firmware='' + qemu_firmware='', + log='', ): suite_path = pathlib.Path(SPEC_TEST_DIR).resolve() if not suite_path.exists(): @@ -240,6 +271,10 @@ def test_suite( simd_case_list = sorted(suite_path.glob("simd/*.wast")) case_list.extend(simd_case_list) + if gc_flag: + gc_case_list = sorted(suite_path.glob("gc/*.wast")) + case_list.extend(gc_case_list) + case_count = len(case_list) failed_case = 0 successful_case = 0 @@ -262,8 +297,10 @@ def test_suite( xip_flag, clean_up_flag, verbose_flag, + gc_flag, qemu_flag, qemu_firmware, + log, ], ) @@ -297,8 +334,10 @@ def test_suite( xip_flag, clean_up_flag, verbose_flag, + gc_flag, qemu_flag, qemu_firmware, + log, ) successful_case += 1 except Exception as e: @@ -393,6 +432,12 @@ def main(): dest="qemu_firmware", help="Firmware required by qemu", ) + parser.add_argument( + "--log", + default='', + dest="log", + help="Log directory", + ) parser.add_argument( "--quiet", action="store_false", @@ -400,6 +445,13 @@ def main(): dest="verbose_flag", help="Close real time output while running cases, only show last words of failed ones", ) + parser.add_argument( + "--gc", + action="store_true", + default=False, + dest="gc_flag", + help="Running with GC feature", + ) parser.add_argument( "cases", metavar="path_to__case", @@ -432,9 +484,11 @@ def main(): options.xip_flag, options.clean_up_flag, options.verbose_flag, + options.gc_flag, options.parl_flag, options.qemu_flag, options.qemu_firmware, + options.log, ) end = time.time_ns() print( @@ -454,8 +508,10 @@ def main(): options.xip_flag, options.clean_up_flag, options.verbose_flag, + options.gc_flag, options.qemu_flag, - options.qemu_firmware + options.qemu_firmware, + options.log ) else: ret = True diff --git a/tests/wamr-test-suites/spec-test-script/collect_coverage.sh b/tests/wamr-test-suites/spec-test-script/collect_coverage.sh new file mode 100755 index 000000000..09b1f465e --- /dev/null +++ b/tests/wamr-test-suites/spec-test-script/collect_coverage.sh @@ -0,0 +1,82 @@ +#!/usr/bin/env bash + +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +readonly WORK_DIR=$PWD +readonly WAMR_DIR=${WORK_DIR}/../../.. +readonly DST_COV_FILE=$1 +readonly SRC_COV_DIR=$2 +readonly SRC_TEMP_COV_FILE=wamr_temp.lcov +readonly SRC_COV_FILE=wamr.lcov + +# get dest folder +dir=$(dirname ${DST_COV_FILE}) +pushd ${dir} > /dev/null 2>&1 +readonly DST_COV_DIR=${PWD} +popd > /dev/null 2>&1 + +if [[ ! -d ${SRC_COV_DIR} ]]; then + echo "${SRC_COV_DIR} doesn't exist, ignore code coverage collection" + exit +fi + +echo "Start to collect code coverage of ${SRC_COV_DIR} .." + +pushd ${SRC_COV_DIR} > /dev/null 2>&1 + +# collect all code coverage data +lcov -q -o ${SRC_TEMP_COV_FILE} -c -d . --rc lcov_branch_coverage=1 +# extract code coverage data of WAMR source files +lcov -q -r ${SRC_TEMP_COV_FILE} -o ${SRC_TEMP_COV_FILE} \ + -rc lcov_branch_coverage=1 \ + "*/usr/*" "*/_deps/*" "*/deps/*" "*/tests/unit/*" \ + "*/llvm/include/*" "*/include/llvm/*" "*/samples/*" \ + "*/app-framework/*" "*/app-mgr/*" "*/test-tools/*" \ + "*/tests/standalone/*" "*/tests/*" + +if [[ -s ${SRC_TEMP_COV_FILE} ]]; then + if [[ -s ${DST_COV_FILE} ]]; then + # merge code coverage data + lcov --rc lcov_branch_coverage=1 \ + --add-tracefile ${SRC_TEMP_COV_FILE} \ + -a ${DST_COV_FILE} -o ${SRC_COV_FILE} + # backup the original lcov file + cp -a ${DST_COV_FILE} "${DST_COV_FILE}.orig" + # replace the lcov file + cp -a ${SRC_COV_FILE} ${DST_COV_FILE} + echo "Code coverage file ${DST_COV_FILE} was appended" + else + cp -a ${SRC_TEMP_COV_FILE} ${SRC_COV_FILE} + cp -a ${SRC_COV_FILE} ${DST_COV_FILE} + echo "Code coverage file ${DST_COV_FILE} was generated" + fi + + # get ignored prefix path + dir=$(dirname ${WAMR_DIR}/../..) + pushd ${dir} > /dev/null 2>&1 + prefix_full_path=${PWD} + popd > /dev/null 2>&1 + + # generate html output for merged code coverage data + rm -fr ${DST_COV_DIR}/wamr-lcov + genhtml -q -t "WAMR Code Coverage" \ + --rc lcov_branch_coverage=1 --prefix=${prefix_full_path} \ + -o ${DST_COV_DIR}/wamr-lcov \ + ${DST_COV_FILE} + + cd ${DST_COV_DIR} + rm -f wamr-lcov.zip + zip -r -q -o wamr-lcov.zip wamr-lcov + rm -fr wamr-lcov + + echo "Code coverage html ${DST_COV_DIR}/wamr-lcov.zip was generated" +else + echo "generate code coverage html failed" +fi + +echo "" + +popd > /dev/null 2>&1 diff --git a/tests/wamr-test-suites/spec-test-script/runtest.py b/tests/wamr-test-suites/spec-test-script/runtest.py index a61f03b1d..a1e505bd0 100755 --- a/tests/wamr-test-suites/spec-test-script/runtest.py +++ b/tests/wamr-test-suites/spec-test-script/runtest.py @@ -38,7 +38,7 @@ log_file = None temp_file_repo = [] # to save the mapping of module files in /tmp by name -temp_module_table = {} +temp_module_table = {} def debug(data): if debug_file: @@ -200,6 +200,8 @@ parser.add_argument('--no-pty', action='store_true', help="Use direct pipes instead of pseudo-tty") parser.add_argument('--log-file', type=str, help="Write messages to the named file in addition the screen") +parser.add_argument('--log-dir', type=str, + help="The log directory to save the case file if test failed") parser.add_argument('--debug-file', type=str, help="Write all test interaction the named file") @@ -228,6 +230,9 @@ parser.add_argument('--multi-module', default=False, action='store_true', parser.add_argument('--multi-thread', default=False, action='store_true', help="Enable Multi-thread") +parser.add_argument('--gc', default=False, action='store_true', + help='Test with GC') + parser.add_argument('--qemu', default=False, action='store_true', help="Enable QEMU") @@ -418,11 +423,20 @@ def parse_simple_const_w_type(number, type): number = float.fromhex(number) if '0x' in number else float(number) return number, "{:.7g}:{}".format(number, type) elif type == "ref.null": - # hard coding - return "extern", "extern:ref.null" + if number == "func": + return "func", "func:ref.null" + elif number == "extern": + return "extern", "extern:ref.null" + elif number == "any": + return "any", "any:ref.null" + else: + raise Exception("invalid value {} and type {}".format(number, type)) elif type == "ref.extern": number = int(number, 16) if '0x' in number else int(number) return number, "0x{:x}:ref.extern".format(number) + elif type == "ref.host": + number = int(number, 16) if '0x' in number else int(number) + return number, "0x{:x}:ref.host".format(number) else: raise Exception("invalid value {} and type {}".format(number, type)) @@ -438,6 +452,10 @@ def parse_assertion_value(val): type.const val ref.extern val ref.null ref_type + ref.array + ref.struct + ref.func + ref.i31 """ if not val: return None, "" @@ -451,6 +469,8 @@ def parse_assertion_value(val): if type in ["i32", "i64", "f32", "f64"]: return parse_simple_const_w_type(numbers[0], type) elif type == "ref": + if splitted[0] in ["ref.array", "ref.struct", "ref.func", "ref.i31"]: + return splitted[0] # need to distinguish between "ref.null" and "ref.extern" return parse_simple_const_w_type(numbers[0], splitted[0]) else: @@ -613,6 +633,9 @@ def simple_value_comparison(out, expected): elif "ref.extern" == expected_type: out_val_binary = out_val expected_val_binary = expected_val + elif "ref.host" == expected_type: + out_val_binary = out_val + expected_val_binary = expected_val else: assert(0), "unknown 'expected_type' {}".format(expected_type) @@ -635,8 +658,10 @@ def value_comparison(out, expected): if not expected: return False - assert(':' in out), "out should be in a form likes numbers:type, but {}".format(out) - assert(':' in expected), "expected should be in a form likes numbers:type, but {}".format(expected) + if not out in ["ref.array", "ref.struct", "ref.func", "ref.any", "ref.i31"]: + assert(':' in out), "out should be in a form likes numbers:type, but {}".format(out) + if not expected in ["ref.array", "ref.struct", "ref.func", "ref.any", "ref.i31"]: + assert(':' in expected), "expected should be in a form likes numbers:type, but {}".format(expected) if 'v128' in out: return vector_value_comparison(out, expected) @@ -759,6 +784,9 @@ def test_assert_return(r, opts, form): elif "ref.extern" == splitted[0]: number, _ = parse_simple_const_w_type(splitted[1], splitted[0]) args.append(str(number)) + elif "ref.host" == splitted[0]: + number, _ = parse_simple_const_w_type(splitted[1], splitted[0]) + args.append(str(number)) else: assert(0), "an unkonwn parameter type" @@ -767,7 +795,15 @@ def test_assert_return(r, opts, form): else: returns = re.split("\)\s*\(", m.group(3)[1:-1]) # processed numbers in strings - expected = [parse_assertion_value(v)[1] for v in returns] + if len(returns) == 1 and returns[0] in ["ref.array", "ref.struct", "ref.i31", + "ref.eq", "ref.any", "ref.extern", + "ref.func", "ref.null"]: + expected = [returns[0]] + elif len(returns) == 1 and returns[0] in ["func:ref.null", "any:ref.null", + "extern:ref.null"]: + expected = [returns[0]] + else: + expected = [parse_assertion_value(v)[1] for v in returns] expected = ",".join(expected) test_assert(r, opts, "return", "%s %s" % (func, " ".join(args)), expected) @@ -798,10 +834,10 @@ def test_assert_return(r, opts, form): if n.group(3) == '': args=[] else: - args = [re.split(' +', v)[1] for v in re.split("\)\s*\(", n.group(3)[1:-1])] - - # a workaround for "ref.null extern" and "ref.null func" - args = [ arg.replace('extern', 'null').replace('func', 'null') for arg in args] + # convert (ref.null extern/func) into (ref.null null) + n1 = n.group(3).replace("(ref.null extern)", "(ref.null null)") + n1 = n1.replace("ref.null func)", "(ref.null null)") + args = [re.split(' +', v)[1] for v in re.split("\)\s*\(", n1[1:-1])] _, expected = parse_assertion_value(n.group(4)[1:-1]) test_assert(r, opts, "return", "%s %s" % (func, " ".join(args)), expected) @@ -826,10 +862,10 @@ def test_assert_trap(r, opts, form): if m.group(2) == '': args = [] else: - args = [re.split(' +', v)[1] for v in re.split("\)\s*\(", m.group(2)[1:-1])] - - # workaround for "ref.null extern" - args = [ arg.replace('extern', 'null').replace('func', 'null') for arg in args] + # convert (ref.null extern/func) into (ref.null null) + m1 = m.group(2).replace("(ref.null extern)", "(ref.null null)") + m1 = m1.replace("ref.null func)", "(ref.null null)") + args = [re.split(' +', v)[1] for v in re.split("\)\s*\(", m1[1:-1])] expected = "Exception: %s" % m.group(3) test_assert(r, opts, "trap", "%s %s" % (func, " ".join(args)), expected) @@ -916,10 +952,11 @@ def compile_wast_to_wasm(form, wast_tempfile, wasm_tempfile, opts): log("Compiling WASM to '%s'" % wasm_tempfile) # default arguments - cmd = [opts.wast2wasm, - "--enable-thread", - "--no-check", - wast_tempfile, "-o", wasm_tempfile ] + if opts.gc: + cmd = [opts.wast2wasm, "-u", "-d", wast_tempfile, "-o", wasm_tempfile] + else: + cmd = [opts.wast2wasm, "--enable-thread", "--no-check", + wast_tempfile, "-o", wasm_tempfile ] # remove reference-type and bulk-memory enabling options since a WABT # commit 30c1e983d30b33a8004b39fd60cbd64477a7956c @@ -1021,18 +1058,18 @@ def run_wasm_with_repl(wasm_tempfile, aot_tempfile, opts, r): if (r != None): r.cleanup() r = Runner(cmd, no_pty=opts.no_pty) - + if opts.qemu: r.read_to_prompt(['nsh> '], 10) r.writeline("mount -t hostfs -o fs={} /tmp".format(tempfile.gettempdir())) r.read_to_prompt(['nsh> '], 10) r.writeline(" ".join(cmd_iwasm)) - + return r def create_tmpfiles(wast_name): tempfiles = [] - + (t1fd, wast_tempfile) = tempfile.mkstemp(suffix=".wast") (t2fd, wasm_tempfile) = tempfile.mkstemp(suffix=".wasm") tempfiles.append(wast_tempfile) @@ -1092,6 +1129,7 @@ def test_assert_with_exception(form, wast_tempfile, wasm_tempfile, aot_tempfile, if __name__ == "__main__": opts = parser.parse_args(sys.argv[1:]) + print('Input param :',opts) if opts.aot: test_aot = True # default x86_64 @@ -1271,12 +1309,16 @@ if __name__ == "__main__": print("THE FINAL EXCEPTION IS {}".format(e)) ret_code = 101 + shutil.copyfile(wasm_tempfile, os.path.join(opts.log_dir, os.path.basename(wasm_tempfile))) + if opts.aot or opts.xip: + shutil.copyfile(aot_tempfile, os.path.join(opts.log_dir,os.path.basename(aot_tempfile))) if "indirect-mode" in str(e): compile_wasm_to_aot(wasm_tempfile, aot_tempfile, None, opts, None, "object") + shutil.copyfile(aot_tempfile, os.path.join(opts.log_dir,os.path.basename(aot_tempfile)+'.o')) subprocess.check_call(["llvm-objdump", "-r", aot_tempfile]) compile_wasm_to_aot(wasm_tempfile, aot_tempfile, None, opts, None, "ir") - subprocess.check_call(["cat", aot_tempfile]) + shutil.copyfile(aot_tempfile, os.path.join(opts.log_dir,os.path.basename(aot_tempfile)+".ir")) else: ret_code = 0 diff --git a/tests/wamr-test-suites/test_wamr.sh b/tests/wamr-test-suites/test_wamr.sh index dca7f87c8..cf0bc970b 100755 --- a/tests/wamr-test-suites/test_wamr.sh +++ b/tests/wamr-test-suites/test_wamr.sh @@ -14,18 +14,23 @@ function help() { echo "test_wamr.sh [options]" echo "-c clean previous test results, not start test" - echo "-s {suite_name} test only one suite (spec)" - echo "-m set compile target of iwasm(x86_64\x86_32\armv7_vfp\thumbv7_vfp\riscv64_lp64d\riscv64_lp64)" - echo "-t set compile type of iwasm(classic-interp\fast-interp\jit\aot\fast-jit\multi-tier-jit)" + echo "-s {suite_name} test only one suite (spec|wasi_certification)" + echo "-m set compile target of iwasm(x86_64|x86_32|armv7_vfp|thumbv7_vfp|riscv64_lp64d|riscv64_lp64)" + echo "-t set compile type of iwasm(classic-interp|fast-interp|jit|aot|fast-jit|multi-tier-jit)" echo "-M enable multi module feature" echo "-p enable multi thread feature" echo "-S enable SIMD feature" + echo "-G enable GC feature" echo "-X enable XIP feature" echo "-x test SGX" + echo "-w enable WASI threads" echo "-b use the wabt binary release package instead of compiling from the source code" + echo "-g build iwasm with debug version" + echo "-v enable GC heap verification" echo "-P run the spec test parallelly" echo "-Q enable qemu" echo "-F set the firmware path used by qemu" + echo "-C enable code coverage collect" } OPT_PARSED="" @@ -34,11 +39,15 @@ WABT_BINARY_RELEASE="NO" TYPE=("classic-interp" "fast-interp" "jit" "aot" "fast-jit" "multi-tier-jit") #default target TARGET="X86_64" +ENABLE_WASI_THREADS=0 ENABLE_MULTI_MODULE=0 ENABLE_MULTI_THREAD=0 COLLECT_CODE_COVERAGE=0 ENABLE_SIMD=0 +ENABLE_GC=0 ENABLE_XIP=0 +ENABLE_DEBUG_VERSION=0 +ENABLE_GC_HEAP_VERIFY=0 #unit test case arrary TEST_CASE_ARR=() SGX_OPT="" @@ -46,8 +55,9 @@ PLATFORM=$(uname -s | tr A-Z a-z) PARALLELISM=0 ENABLE_QEMU=0 QEMU_FIRMWARE="" +WASI_TESTSUITE_COMMIT="aca78d919355ae00af141e6741a439039615b257" -while getopts ":s:cabt:m:MCpSXxPQF:" opt +while getopts ":s:cabgvt:m:MCpSXxwPGQF:" opt do OPT_PARSED="TRUE" case $opt in @@ -69,8 +79,9 @@ do c) read -t 5 -p "Are you sure to delete all reports. y/n " cmd if [[ $cmd == "y" && $(ls -A workspace/report) ]];then - rm -r workspace/report/* - echo "cleaned all reports" + rm -fr workspace/report/* + rm -fr /tmp/*.wasm /tmp/*.wast /tmp/*.aot + echo "cleaned all reports and temp files" fi exit 0;; a) @@ -97,6 +108,10 @@ do echo "set compile target of wamr" ${OPTARG} TARGET=${OPTARG^^} # set target to uppercase if input x86_32 or x86_64 --> X86_32 and X86_64 ;; + w) + echo "enable WASI threads" + ENABLE_WASI_THREADS=1 + ;; M) echo "enable multi module feature" ENABLE_MULTI_MODULE=1 @@ -121,6 +136,18 @@ do echo "test SGX" SGX_OPT="--sgx" ;; + g) + echo "enable build iwasm with debug version" + ENABLE_DEBUG_VERSION=1 + ;; + v) + echo "enable GC heap verification" + ENABLE_GC_HEAP_VERIFY=1 + ;; + G) + echo "enable GC feature" + ENABLE_GC=1 + ;; P) PARALLELISM=1 ;; @@ -157,7 +184,6 @@ readonly DATE=$(date +%Y-%m-%d_%H:%M:%S) readonly REPORT_DIR=${WORK_DIR}/report/${DATE} mkdir -p ${REPORT_DIR} -# TODO: a strong assumation about a link to the WAMR project readonly WAMR_DIR=${WORK_DIR}/../../.. if [[ ${SGX_OPT} == "--sgx" ]];then @@ -191,14 +217,16 @@ readonly ORC_EAGER_JIT_COMPILE_FLAGS="\ -DWAMR_BUILD_INTERP=0 -DWAMR_BUILD_FAST_INTERP=0 \ -DWAMR_BUILD_JIT=1 -DWAMR_BUILD_AOT=1 \ -DWAMR_BUILD_LAZY_JIT=0 \ - -DWAMR_BUILD_SPEC_TEST=1" + -DWAMR_BUILD_SPEC_TEST=1 \ + -DCOLLECT_CODE_COVERAGE=${COLLECT_CODE_COVERAGE}" readonly ORC_LAZY_JIT_COMPILE_FLAGS="\ -DWAMR_BUILD_TARGET=${TARGET} \ -DWAMR_BUILD_INTERP=0 -DWAMR_BUILD_FAST_INTERP=0 \ -DWAMR_BUILD_JIT=1 -DWAMR_BUILD_AOT=1 \ -DWAMR_BUILD_LAZY_JIT=1 \ - -DWAMR_BUILD_SPEC_TEST=1" + -DWAMR_BUILD_SPEC_TEST=1 \ + -DCOLLECT_CODE_COVERAGE=${COLLECT_CODE_COVERAGE}" readonly AOT_COMPILE_FLAGS="\ -DWAMR_BUILD_TARGET=${TARGET} \ @@ -212,13 +240,15 @@ readonly FAST_JIT_COMPILE_FLAGS="\ -DWAMR_BUILD_INTERP=1 -DWAMR_BUILD_FAST_INTERP=0 \ -DWAMR_BUILD_JIT=0 -DWAMR_BUILD_AOT=0 \ -DWAMR_BUILD_FAST_JIT=1 \ - -DWAMR_BUILD_SPEC_TEST=1" + -DWAMR_BUILD_SPEC_TEST=1 \ + -DCOLLECT_CODE_COVERAGE=${COLLECT_CODE_COVERAGE}" readonly MULTI_TIER_JIT_COMPILE_FLAGS="\ -DWAMR_BUILD_TARGET=${TARGET} \ -DWAMR_BUILD_INTERP=1 -DWAMR_BUILD_FAST_INTERP=0 \ -DWAMR_BUILD_FAST_JIT=1 -DWAMR_BUILD_JIT=1 \ - -DWAMR_BUILD_SPEC_TEST=1" + -DWAMR_BUILD_SPEC_TEST=1 \ + -DCOLLECT_CODE_COVERAGE=${COLLECT_CODE_COVERAGE}" readonly COMPILE_FLAGS=( "${CLASSIC_INTERP_COMPILE_FLAGS}" @@ -230,39 +260,19 @@ readonly COMPILE_FLAGS=( "${MULTI_TIER_JIT_COMPILE_FLAGS}" ) -# TODO: with libiwasm.so only function unit_test() { echo "Now start unit tests" cd ${WORK_DIR} - readonly UNIT_CASES="wasm-vm host-tool utils" + rm -fr unittest-build && mkdir unittest-build + cd unittest-build echo "Build unit test" touch ${REPORT_DIR}/unit_test_report.txt - - for compile_flag in "${COMPILE_FLAGS[@]}"; do - echo "Build unit test with compile flags with " ${compile_flag} - - # keep going and do not care if it is success or not - make -ki clean | true - cmake ${compile_flag} ${WORK_DIR}/../../unit && make -j 4 - if [ "$?" != 0 ];then - echo -e "build unit test failed, you may need to change wamr into dev/aot branch and ensure llvm is built" - exit 1 - fi - - echo ${compile_flag} >> ${REPORT_DIR}/unit_test_report.txt - - for case in ${UNIT_CASES} - do - echo "run ${case} ..." - cd ./${case}/ - ./${case/-/_}"_test" | tee -a ${REPORT_DIR}/unit_test_report.txt - cd - - echo "finish ${case}" - done - done + cmake ${WORK_DIR}/../../unit -DCOLLECT_CODE_COVERAGE=${COLLECT_CODE_COVERAGE} + make -j + make test | tee -a ${REPORT_DIR}/unit_test_report.txt echo "Finish unit tests" } @@ -340,6 +350,27 @@ function spec_test() git apply ../../spec-test-script/thread_proposal_fix_atomic_case.patch fi + # update GC cases + if [[ ${ENABLE_GC} == 1 ]]; then + echo "checkout spec for GC proposal" + + popd + rm -fr spec + # check spec test cases for GC + git clone -b main --single-branch https://github.com/WebAssembly/gc.git spec + pushd spec + + git restore . && git clean -ffd . + # Sync constant expression descriptions + git reset --hard 62beb94ddd41987517781732f17f213d8b866dcc + git apply ../../spec-test-script/gc_ignore_cases.patch + + echo "compile the reference intepreter" + pushd interpreter + make opt + popd + fi + popd echo $(pwd) @@ -359,16 +390,16 @@ function spec_test() exit 1 ;; esac - if [ ! -f /tmp/wabt-1.0.29-${WABT_PLATFORM}.tar.gz ]; then + if [ ! -f /tmp/wabt-1.0.31-${WABT_PLATFORM}.tar.gz ]; then wget \ - https://github.com/WebAssembly/wabt/releases/download/1.0.29/wabt-1.0.29-${WABT_PLATFORM}.tar.gz \ + https://github.com/WebAssembly/wabt/releases/download/1.0.31/wabt-1.0.31-${WABT_PLATFORM}.tar.gz \ -P /tmp fi cd /tmp \ - && tar zxf wabt-1.0.29-${WABT_PLATFORM}.tar.gz \ + && tar zxf wabt-1.0.31-${WABT_PLATFORM}.tar.gz \ && mkdir -p ${WORK_DIR}/wabt/out/gcc/Release/ \ - && install wabt-1.0.29/bin/wa* ${WORK_DIR}/wabt/out/gcc/Release/ \ + && install wabt-1.0.31/bin/wa* ${WORK_DIR}/wabt/out/gcc/Release/ \ && cd - fi else @@ -428,7 +459,7 @@ function spec_test() fi # set the current running target - ARGS_FOR_SPEC_TEST+="-m ${TARGET} " + ARGS_FOR_SPEC_TEST+="-m ${TARGET} " # require warmc only in aot mode if [[ $1 == 'aot' ]]; then @@ -439,15 +470,25 @@ function spec_test() ARGS_FOR_SPEC_TEST+="--parl " fi + if [[ ${ENABLE_GC} == 1 ]]; then + ARGS_FOR_SPEC_TEST+="--gc " + fi + if [[ ${ENABLE_QEMU} == 1 ]]; then ARGS_FOR_SPEC_TEST+="--qemu " - ARGS_FOR_SPEC_TEST+="--qemu-firmware ${QEMU_FIRMWARE}" + ARGS_FOR_SPEC_TEST+="--qemu-firmware ${QEMU_FIRMWARE} " fi + # set log directory + ARGS_FOR_SPEC_TEST+="--log ${REPORT_DIR}" + cd ${WORK_DIR} echo "python3 ./all.py ${ARGS_FOR_SPEC_TEST} | tee -a ${REPORT_DIR}/spec_test_report.txt" python3 ./all.py ${ARGS_FOR_SPEC_TEST} | tee -a ${REPORT_DIR}/spec_test_report.txt - [[ ${PIPESTATUS[0]} -ne 0 ]] && exit 1 + if [[ ${PIPESTATUS[0]} -ne 0 ]];then + echo -e "\nspec tests FAILED" | tee -a ${REPORT_DIR}/spec_test_report.txt + exit 1 + fi cd - echo -e "\nFinish spec tests" | tee -a ${REPORT_DIR}/spec_test_report.txt @@ -469,6 +510,30 @@ function wasi_test() echo "Finish wasi tests" } +function wasi_certification_test() +{ + echo "Now start wasi certification tests" + + cd ${WORK_DIR} + if [ ! -d "wasi-testsuite" ]; then + echo "wasi not exist, clone it from github" + git clone -b prod/testsuite-all \ + --single-branch https://github.com/WebAssembly/wasi-testsuite.git + fi + cd wasi-testsuite + git reset --hard ${WASI_TESTSUITE_COMMIT} + + bash ../../wasi-test-script/run_wasi_tests.sh $1 $TARGET \ + | tee -a ${REPORT_DIR}/wasi_test_report.txt + ret=${PIPESTATUS[0]} + + if [[ ${ret} -ne 0 ]];then + echo -e "\nwasi tests FAILED" | tee -a ${REPORT_DIR}/wasi_test_report.txt + exit 1 + fi + echo -e "\nFinish wasi tests" | tee -a ${REPORT_DIR}/wasi_test_report.txt +} + function polybench_test() { echo "Now start polybench tests" @@ -477,7 +542,6 @@ function polybench_test() if [[ $1 == "aot" || $1 == "jit" ]];then ./build.sh AOT ${SGX_OPT} ./test_aot.sh $1 ${SGX_OPT} - else ./build.sh ./test_interp.sh ${SGX_OPT} @@ -487,6 +551,22 @@ function polybench_test() echo "Finish polybench tests" } +function libsodium_test() +{ + echo "Now start libsodium tests" + + cd ${WORK_DIR}/../libsodium + if [[ $1 == "aot" || $1 == "jit" ]];then + ./build.sh ${SGX_OPT} + ./test_aot.sh $1 ${SGX_OPT} + else + ./test_interp.sh ${SGX_OPT} + fi + cp report.txt ${REPORT_DIR}/libsodium_$1_test_report.txt + + echo "Finish libsodium tests" +} + function malformed_test() { # build iwasm firstly @@ -494,20 +574,64 @@ function malformed_test() ./malformed_test.py --run ${IWASM_CMD} | tee ${REPORT_DIR}/malfomed_$1_test_report.txt } +function collect_standalone() +{ + if [[ ${COLLECT_CODE_COVERAGE} == 1 ]]; then + pushd ${WORK_DIR} > /dev/null 2>&1 + + CODE_COV_FILE="" + if [[ -z "${CODE_COV_FILE}" ]]; then + CODE_COV_FILE="${WORK_DIR}/wamr.lcov" + else + CODE_COV_FILE="${CODE_COV_FILE}" + fi + + STANDALONE_DIR=${WORK_DIR}/../../standalone + + echo "Collect code coverage of standalone dump-call-stack" + ./collect_coverage.sh "${CODE_COV_FILE}" "${STANDALONE_DIR}/dump-call-stack/build" + echo "Collect code coverage of standalone dump-mem-profiling" + ./collect_coverage.sh "${CODE_COV_FILE}" "${STANDALONE_DIR}/dump-mem-profiling/build" + echo "Collect code coverage of standalone dump-perf-profiling" + ./collect_coverage.sh "${CODE_COV_FILE}" "${STANDALONE_DIR}/dump-perf-profiling/build" + if [[ $1 == "aot" ]]; then + echo "Collect code coverage of standalone pad-test" + ./collect_coverage.sh "${CODE_COV_FILE}" "${STANDALONE_DIR}/pad-test/build" + fi + echo "Collect code coverage of standalone test-invoke-native" + ./collect_coverage.sh "${CODE_COV_FILE}" "${STANDALONE_DIR}/test-invoke-native/build" + echo "Collect code coverage of standalone test-running-modes" + ./collect_coverage.sh "${CODE_COV_FILE}" "${STANDALONE_DIR}/test-running-modes/build" + echo "Collect code coverage of standalone test-running-modes/c-embed" + ./collect_coverage.sh "${CODE_COV_FILE}" "${STANDALONE_DIR}/test-running-modes/c-embed/build" + echo "Collect code coverage of standalone test-ts2" + ./collect_coverage.sh "${CODE_COV_FILE}" "${STANDALONE_DIR}/test-ts2/build" + + popd > /dev/null 2>&1 + fi +} + function standalone_test() { - cd ${WORK_DIR}/../../standalone - - args="" - - [[ $1 == "aot" ]] && args="$args --aot" || args="$args --no-aot" - [[ ${SGX_OPT} == "--sgx" ]] && args="$args --sgx" || args="$args --no-sgx" - - if [[ ${ENABLE_MULTI_THREAD} == 1 ]];then - args="$args --thread" + if [[ ${COLLECT_CODE_COVERAGE} == 1 ]]; then + export COLLECT_CODE_COVERAGE=1 fi + cd ${WORK_DIR}/../../standalone + + args="--$1" + + [[ ${SGX_OPT} == "--sgx" ]] && args="$args --sgx" || args="$args --no-sgx" + + [[ ${ENABLE_MULTI_THREAD} == 1 ]] && args="$args --thread" || args="$args --no-thread" + + [[ ${ENABLE_SIMD} == 1 ]] && args="$args --simd" || args="$args --no-simd" + + args="$args ${TARGET}" + ./standalone.sh $args | tee ${REPORT_DIR}/standalone_$1_test_report.txt + + collect_standalone "$1" } function build_iwasm_with_cfg() @@ -552,7 +676,7 @@ function build_wamrc() && ./build_llvm.sh \ && if [ -d build ]; then rm -r build/*; else mkdir build; fi \ && cd build \ - && cmake .. \ + && cmake .. -DCOLLECT_CODE_COVERAGE=${COLLECT_CODE_COVERAGE} \ && make -j 4 } @@ -565,15 +689,33 @@ function build_wamrc() function collect_coverage() { - if [[ ${COLLECT_CODE_COVERAGE} == 1 ]];then - cd ${IWASM_LINUX_ROOT_DIR}/build - lcov -t "iwasm code coverage" -o iwasm.info -c -d . - genhtml -o iwasm-gcov iwasm.info - [[ -d iwasm-gcov ]] && \ - cp -r iwasm-gcov ${REPORT_DIR}/$1_iwasm_gcov || \ - echo "generate code coverage html failed" + if [[ ${COLLECT_CODE_COVERAGE} == 1 ]]; then + ln -sf ${WORK_DIR}/../spec-test-script/collect_coverage.sh ${WORK_DIR} + + CODE_COV_FILE="" + if [[ -z "${CODE_COV_FILE}" ]]; then + CODE_COV_FILE="${WORK_DIR}/wamr.lcov" + else + CODE_COV_FILE="${CODE_COV_FILE}" + fi + + pushd ${WORK_DIR} > /dev/null 2>&1 + echo "Collect code coverage of iwasm" + ./collect_coverage.sh ${CODE_COV_FILE} ${IWASM_LINUX_ROOT_DIR}/build + if [[ $1 == "llvm-aot" ]]; then + echo "Collect code coverage of wamrc" + ./collect_coverage.sh ${CODE_COV_FILE} ${WAMR_DIR}/wamr-compiler/build + fi + for suite in "${TEST_CASE_ARR[@]}"; do + if [[ ${suite} = "unit" ]]; then + echo "Collect code coverage of unit test" + ./collect_coverage.sh ${CODE_COV_FILE} ${WORK_DIR}/unittest-build + break + fi + done + popd > /dev/null 2>&1 else - echo "will not collect code coverage" + echo "code coverage isn't collected" fi } @@ -602,6 +744,24 @@ function trigger() EXTRA_COMPILE_FLAGS+=" -DWAMR_BUILD_SIMD=0" fi + if [[ ${ENABLE_GC} == 1 ]]; then + EXTRA_COMPILE_FLAGS+=" -DWAMR_BUILD_GC=1" + EXTRA_COMPILE_FLAGS+=" -DWAMR_BUILD_REF_TYPES=1" + EXTRA_COMPILE_FLAGS+=" -DWAMR_BUILD_BULK_MEMORY=1" + fi + + if [[ ${ENABLE_DEBUG_VERSION} == 1 ]]; then + EXTRA_COMPILE_FLAGS+=" -DCMAKE_BUILD_TYPE=Debug" + fi + + if [[ ${ENABLE_GC_HEAP_VERIFY} == 1 ]]; then + EXTRA_COMPILE_FLAGS+=" -DWAMR_BUILD_GC_HEAP_VERIFY=1" + fi + + if [[ ${ENABLE_WASI_THREADS} == 1 ]]; then + EXTRA_COMPILE_FLAGS+=" -DWAMR_BUILD_LIB_WASI_THREADS=1" + fi + for t in "${TYPE[@]}"; do case $t in "classic-interp") @@ -649,18 +809,18 @@ function trigger() echo "work in orc jit eager compilation mode" BUILD_FLAGS="$ORC_EAGER_JIT_COMPILE_FLAGS $EXTRA_COMPILE_FLAGS" build_iwasm_with_cfg $BUILD_FLAGS - build_wamrc for suite in "${TEST_CASE_ARR[@]}"; do $suite"_test" jit done + collect_coverage llvm-jit echo "work in orc jit lazy compilation mode" BUILD_FLAGS="$ORC_EAGER_JIT_COMPILE_FLAGS $EXTRA_COMPILE_FLAGS" build_iwasm_with_cfg $BUILD_FLAGS - build_wamrc for suite in "${TEST_CASE_ARR[@]}"; do $suite"_test" jit done + collect_coverage llvm-jit ;; "aot") @@ -674,7 +834,7 @@ function trigger() for suite in "${TEST_CASE_ARR[@]}"; do $suite"_test" aot done - collect_coverage aot + collect_coverage llvm-aot ;; "fast-jit") @@ -685,6 +845,7 @@ function trigger() for suite in "${TEST_CASE_ARR[@]}"; do $suite"_test" fast-jit done + collect_coverage fast-jit ;; "multi-tier-jit") @@ -695,6 +856,7 @@ function trigger() for suite in "${TEST_CASE_ARR[@]}"; do $suite"_test" multi-tier-jit done + collect_coverage multi-tier-jit ;; *) @@ -705,11 +867,19 @@ function trigger() } # if collect code coverage, ignore -s, test all test cases. -if [[ $TEST_CASE_ARR && $COLLECT_CODE_COVERAGE != 1 ]];then +if [[ $TEST_CASE_ARR ]];then trigger || (echo "TEST FAILED"; exit 1) else - # test all suite, ignore polybench because of long time cost - TEST_CASE_ARR=("sightglass" "spec" "wasi" "malformed" "standalone") + # test all suite, ignore polybench and libsodium because of long time cost + TEST_CASE_ARR=("spec") + : ' + if [[ $COLLECT_CODE_COVERAGE == 1 ]];then + # add polybench if collecting code coverage data + TEST_CASE_ARR+=("polybench") + # add libsodium if needed, which takes long time to run + TEST_CASE_ARR+=("libsodium") + fi + ' trigger || (echo "TEST FAILED"; exit 1) # Add more suites here fi diff --git a/tests/wamr-test-suites/wasi-test-script/run_wasi_tests.sh b/tests/wamr-test-suites/wasi-test-script/run_wasi_tests.sh new file mode 100755 index 000000000..30d32d5f8 --- /dev/null +++ b/tests/wamr-test-suites/wasi-test-script/run_wasi_tests.sh @@ -0,0 +1,59 @@ +#!/bin/bash + +# +# Copyright (C) 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +readonly MODE=$1 +readonly TARGET=$2 + +readonly WORK_DIR=$PWD +readonly PLATFORM=$(uname -s | tr A-Z a-z) +readonly WAMR_DIR="${WORK_DIR}/../../../.." +readonly IWASM_CMD="${WORK_DIR}/../../../../product-mini/platforms/${PLATFORM}/build/iwasm" +readonly WAMRC_CMD="${WORK_DIR}/../../../../wamr-compiler/build/wamrc" + +if [[ $MODE != "aot" ]];then + python3 -m venv wasi-env && source wasi-env/bin/activate + python3 -m pip install -r test-runner/requirements.txt + TEST_RUNTIME_EXE="${IWASM_CMD}" python3 test-runner/wasi_test_runner.py \ + -r adapters/wasm-micro-runtime.py \ + -t \ + tests/c/testsuite/ \ + tests/assemblyscript/testsuite/ \ + tests/proposals/wasi-threads/ \ + ${WAMR_DIR}/core/iwasm/libraries/lib-wasi-threads/test/ \ + ${WAMR_DIR}/core/iwasm/libraries/lib-socket/test/ \ + exit_code=${PIPESTATUS[0]} + deactivate +else + target_option="" + if [[ $TARGET == "X86_32" ]];then + target_option="--target=i386" + fi + + # Run WASI thread proposal tests + exit_code=0 + wasm_tests=$(ls tests/proposals/wasi-threads/*.wasm) + for test_wasm in ${wasm_tests}; do + test_aot="${test_wasm%.wasm}.aot" + test_json="${test_wasm%.wasm}.json" + + echo "Compiling $test_wasm to $test_aot" + ${WAMRC_CMD} --enable-multi-thread ${target_option} \ + -o $test_aot $test_wasm + + echo "Running $test_aot" + expected=$(jq .exit_code ${test_json}) + ${IWASM_CMD} $test_aot + ret=${PIPESTATUS[0]} + + echo "expected=$expected, actual=$ret" + if [[ $expected != "" ]] && [[ $expected != $ret ]];then + exit_code=1 + fi + done +fi + +exit ${exit_code} \ No newline at end of file diff --git a/wamr-compiler/CMakeLists.txt b/wamr-compiler/CMakeLists.txt index 75c85a77a..0ae821af6 100644 --- a/wamr-compiler/CMakeLists.txt +++ b/wamr-compiler/CMakeLists.txt @@ -44,6 +44,7 @@ add_definitions(-DWASM_ENABLE_CUSTOM_NAME_SECTION=1) add_definitions(-DWASM_ENABLE_DUMP_CALL_STACK=1) add_definitions(-DWASM_ENABLE_PERF_PROFILING=1) add_definitions(-DWASM_ENABLE_LOAD_CUSTOM_SECTION=1) +add_definitions(-DWASM_ENABLE_LIB_WASI_THREADS=1) if (WAMR_BUILD_LLVM_LEGACY_PM EQUAL 1) add_definitions(-DWASM_ENABLE_LLVM_LEGACY_PM=1) @@ -165,6 +166,12 @@ if (WAMR_BUILD_DEBUG_AOT EQUAL 1) message(STATUS "find lldb ${LLDB_ALL_PLUGINS} in: ${LLVM_LIBRARY_DIRS}") endif() +if ("$ENV{COLLECT_CODE_COVERAGE}" STREQUAL "1" OR COLLECT_CODE_COVERAGE EQUAL 1) + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage") + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") + message ("-- Collect code coverage enabled") +endif () + if (NOT (CMAKE_C_COMPILER MATCHES ".*clang.*" OR CMAKE_C_COMPILER_ID MATCHES ".*Clang")) if(NOT MSVC) set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") @@ -204,6 +211,7 @@ if (NOT MINGW) endif() endif() include (${IWASM_DIR}/libraries/lib-pthread/lib_pthread.cmake) +include (${IWASM_DIR}/libraries/lib-wasi-threads/lib_wasi_threads.cmake) include (${IWASM_DIR}/common/iwasm_common.cmake) include (${IWASM_DIR}/interpreter/iwasm_interp.cmake) include (${IWASM_DIR}/aot/iwasm_aot.cmake) @@ -258,6 +266,7 @@ add_library (vmlib ${LIBC_BUILTIN_SOURCE} ${LIBC_WASI_SOURCE} ${LIB_PTHREAD_SOURCE} + ${LIB_WASI_THREADS_SOURCE} ${IWASM_COMMON_SOURCE} ${IWASM_INTERP_SOURCE} ${IWASM_AOT_SOURCE}) diff --git a/wamr-compiler/main.c b/wamr-compiler/main.c index 28986e864..f185a17b7 100644 --- a/wamr-compiler/main.c +++ b/wamr-compiler/main.c @@ -59,6 +59,7 @@ print_help() printf(" --disable-aux-stack-check Disable auxiliary stack overflow/underflow check\n"); printf(" --enable-dump-call-stack Enable stack trace feature\n"); printf(" --enable-perf-profiling Enable function performance profiling\n"); + printf(" --enable-memory-profiling Enable memory usage profiling\n"); printf(" --enable-indirect-mode Enalbe call function through symbol table but not direct call\n"); printf(" --disable-llvm-intrinsics Disable the LLVM built-in intrinsics\n"); printf(" --disable-llvm-lto Disable the LLVM link time optimization\n"); @@ -254,6 +255,9 @@ main(int argc, char *argv[]) else if (!strcmp(argv[0], "--enable-perf-profiling")) { option.enable_aux_stack_frame = true; } + else if (!strcmp(argv[0], "--enable-memory-profiling")) { + option.enable_stack_estimation = true; + } else if (!strcmp(argv[0], "--enable-indirect-mode")) { option.is_indirect_mode = true; } diff --git a/wamr-sdk/app/libc-builtin-sysroot/include/stdint.h b/wamr-sdk/app/libc-builtin-sysroot/include/stdint.h index 8c55bff50..802e9ac5b 100644 --- a/wamr-sdk/app/libc-builtin-sysroot/include/stdint.h +++ b/wamr-sdk/app/libc-builtin-sysroot/include/stdint.h @@ -11,6 +11,13 @@ extern "C" { #endif /* clang-format off */ +/* The word size of platform */ +#ifdef __wasm64__ +#define __WORDSIZE 64 +#else +#define __WORDSIZE 32 +#endif + typedef char int8_t; typedef short int int16_t; typedef int int32_t; @@ -25,22 +32,56 @@ typedef unsigned long long int uint64_t; typedef __INTPTR_TYPE__ intptr_t; typedef __UINTPTR_TYPE__ uintptr_t; +/* Signed and unsigned */ +#if __WORDSIZE == 64 +#define INT64_C(c) c ## L +#define UINT64_C(c) c ## UL +#define INTMAX_C(c) c ## L +#define UINTMAX_C(c) c ## UL +#else +#define INT64_C(c) c ## LL +#define UINT64_C(c) c ## ULL +#define INTMAX_C(c) c ## LL +#define UINTMAX_C(c) c ## ULL +#endif + + /* Minimum of signed integral types. */ # define INT8_MIN (-128) # define INT16_MIN (-32767-1) # define INT32_MIN (-2147483647-1) -# define INT64_MIN (-__INT64_C(9223372036854775807)-1) +# define INT64_MIN (-INT64_C(9223372036854775807)-1) + /* Maximum of signed integral types. */ # define INT8_MAX (127) # define INT16_MAX (32767) # define INT32_MAX (2147483647) -# define INT64_MAX (__INT64_C(9223372036854775807)) +# define INT64_MAX (INT64_C(9223372036854775807)) /* Maximum of unsigned integral types. */ # define UINT8_MAX (255) # define UINT16_MAX (65535) # define UINT32_MAX (4294967295U) -# define UINT64_MAX (__UINT64_C(18446744073709551615)) +# define UINT64_MAX (UINT64_C(18446744073709551615)) + +/* Values to test for integral types holding `void *' pointer. */ +#if __WORDSIZE == 64 +#define INTPTR_MIN INT64_MIN +#define INTPTR_MAX INT64_MAX +#define UINTPTR_MAX UINT64_MAX +#else +#define INTPTR_MIN INT32_MIN +#define INTPTR_MAX INT32_MAX +#define UINTPTR_MAX UINT32_MAX +#endif + +/* Limit of `size_t' type. */ +#if __WORDSIZE == 64 +#define SIZE_MAX UINT64_MAX +#else +#define SIZE_MAX UINT32_MAX +#endif + /* clang-format on */ #ifdef __cplusplus diff --git a/wamr-sdk/build_sdk.sh b/wamr-sdk/build_sdk.sh index 53856e9c3..954584f69 100755 --- a/wamr-sdk/build_sdk.sh +++ b/wamr-sdk/build_sdk.sh @@ -224,8 +224,8 @@ echo -e "\n\n" echo "############## Start to build runtime sdk ###############" cd ${sdk_root}/runtime -rm -fr build_runtime_sdk && mkdir build_runtime_sdk -cd build_runtime_sdk +rm -fr build-runtime-sdk && mkdir build-runtime-sdk +cd build-runtime-sdk cmake .. $CM_DEXTRA_SDK_INCLUDE_PATH \ -DWAMR_BUILD_SDK_PROFILE=${PROFILE} \ -DCONFIG_PATH=${wamr_config_cmake_file} \ @@ -249,6 +249,6 @@ fi cd .. -rm -fr build_runtime_sdk +rm -fr build-runtime-sdk exit 0